剑指offer-Go版实现 第二章:面试需要的基础知识

为何选择这本书来刷题:

leetcode题目实在太多了,找了半天还是回到这本10年前的书,题目数量不多,但是都比较经典,覆盖知识点比较广。开始参加leetcode周赛,做两题都是很难的,尤其是面对一堆大牛动不动四道题全做出来,很受刺激,真的是自惭形秽。狠下心来,只能自己慢慢研究一波,现在稳定两道题,偶尔还能突破三道题,这时候再重新回顾一遍剑指offer第二版,发现,以前死记硬背应对面试的东西,现在可以自己实现出来了。很多同学也说自己算法很差,有的甚至连数组和链表都分不清楚的,所以就试着分享,用GO语言重新实现一遍,也把涉及到的相关知识点一并讲解。

关于第一章的省略说明:

第一章主要介绍面试的环节和流程,感觉跟国内的实际情况有点不相符,毕竟很多并没有这么专业,所以就不打算深究了,主要以面试题的Go版本实现为主,一起相互刷题吧。途中也有很多自己理解不透彻的地方,毕竟,算法方面,我也是个半桶水。

正式开始第二章

面试题1:赋值运算符函数

实在不知道这题的考点是什么,对于任何一种语言来说,字符串的拼接方式很多种,也不会有哪家公司的面试官脑子不开窍问这种题吧。

面试题2:实现单例模式

书上用的是C++,采用的最好方式是静态内部类实现的。GO语言本身来说并没有类一说,也没有内部类一说,init函数实现一个全局的,就相当于实现一个静态的实例,更多的是用sync.Do函数来做单例,实际上,init函数更受用一点,简单方便,只是可能会增加依赖。

面试题3:二维数组中的查找

先附上leetcode地址:leetcode-cn.com/problems/er-wei-sh...
书中都是其他语言的,想要自己实现不可能直接抄代码改写吧,先自己实现一遍还是有必要的,起码自己动了脑,还可以借助leetcode提供的测试用例来验证,实现方式也不一定就是书上提供的。代码解释看注释。

// 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,
//每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,
//输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
func findNumberIn2DArray(matrix [][]int, target int) bool {
    if len(matrix) == 0 {
        return false
    }
    var col = len(matrix[0])//从每一行的最后一个开始查找,组成矩阵
    var row = 0
    for ; row < len(matrix) && col > 0; {
        if matrix[row][col-1] == target {//相等直接返回
            return true
        } else if matrix[row][col-1] > target {//大于,向左移动一列,缩小矩阵范围
            col--
        } else {//小于就向下移动一行,缩小矩阵范围
            row++
        }
    }
    return false
}

面试题4:替换空格

leetcode-cn.com/problems/ti-huan-k...
这道题考查的是数组从后往前坐替换,减少移动次数。但是,算法比赛讲究的是时间,如果面试的时候面试官有要求,就按照书上说的来实现,但是直接使用库函数,leetcode上是接近双百的,要研究的可以自己去把字符串遍历字符数组,第一字符进行替换操作。

func replaceSpace(s string) string {
    return strings.Replace(s, " ", "%20", -1)
}

面试题5:从尾到头打印链表

leetcode-cn.com/problems/cong-wei-...
这个实现方式就更多了,书上说的很全面,面试官沟通清楚是不是可以破坏结构,是不是可以额外添加存储空间。

func reversePrint(head *ListNode) []int {
    res := make([]int, 0)
    if head == nil {
        return res
    }
    for head != nil {
        res = append(res, head.Val)
        head = head.Next
    }
    for i, j := 0, len(res)-1; i < j; {
        res[i], res[j] = res[j], res[i]
        i++
        j--
    }
    return res
}

这里有更简洁的代码,但是效率很低,就是直接在数组前面插入数字,直接返回数组就可以了,由此可见,数组的插入是多么的耗时间。原因和前面二维数组查找的原因一样,前面每次插入所有的元素都要排序一次。

func reversePrint(head *ListNode) []int {
    res := make([]int, 0)
    if head == nil {
        return res
    }
    for head.Next != nil {
        res = append([]int{head.Val}, res...)
        head = head.Next
    }
    res = append([]int{head.Val}, res...)
    return res
}

面试题6:重建二叉树

leetcode-cn.com/problems/zhong-jia...
可以知道,只要和树关联的都是中等难度起步,没有简单的。前序和中序遍历,构建一棵树。面试中不会问到这种问题的,用笔画出来可能更好理解。不会的不用纠结,过段时间回头看发现这都是基础了。另外,递归也要好好研究下,我也用的不是很溜,有时候知道原理也不一定能实现的出来,所以借助leetcode的测试用例,还是很有用的。当然有人取巧,把每个用例都是手动实现一遍,我们是练习,没必要,大牛才这么搞。

func buildTree(preorder []int, inorder []int) *TreeNode {
    if len(preorder) == 0 {
        return nil
    }
    var index = 0
    for ;index < len(inorder); index++{// 遍历中序
        if preorder[0] == inorder[index] {//找到根结点的值位置
            break
        }
    }
    root:=&TreeNode{}
    root.Val = preorder[0]
    root.Left = buildTree(preorder[1:index+1],inorder[0:index])//递归左子树
    root.Right = buildTree(preorder[index+1:],inorder[index+1:])//递归右子树
    return root
}

面试题7:用两个栈实现队列

leetcode-cn.com/problems/yong-lian...
这个提,我觉得完全是动嘴的题目,没有考察代码能力,纯粹的思维考察。有时候不要纠结,该跳过跳过,看看实现原理,不动手也可以的。毕竟这个实现也是添加和删除,想不到该分享的技术实现在哪。用了官网的答案:

type CQueue struct {
    stack1, stack2 *list.List
}

func Constructor() CQueue {
    return CQueue{
        stack1: list.New(),
        stack2: list.New(),
    }
}

func (this *CQueue) AppendTail(value int)  {
    this.stack1.PushBack(value)
}

func (this *CQueue) DeleteHead() int {
    // 如果第二个栈为空
    if this.stack2.Len() == 0 {
        for this.stack1.Len() > 0 {
            this.stack2.PushBack(this.stack1.Remove(this.stack1.Back()))
        }
    }
    if this.stack2.Len() != 0 {
        e := this.stack2.Back()
        this.stack2.Remove(e)
        return e.Value.(int)
    }
    return -1
}

面试题8:旋转数组最小数字

leetcode-cn.com/problems/xuan-zhua...
暴力搜索都可以通过


func minArray(numbers []int) int {
    if len(numbers) == 0 {
        return 0
    }
    min := numbers[0]
    for i := 1; i < len(numbers); i++ {
        if numbers[i-1] > numbers[i] {
            if min > numbers[i] {
                min = numbers[i]
            }
            break
        }
    }
    return min
}

当然,人家看差点肯定不是这个,是如果更快的查找到这个值。因为是组多有两部分排序组成的,依然可以使用二分查找。参考官网的,我也是没想到这么多特例的,low逼一个。如果有人不会,看题解吧,比我解释起来好太多太多。leetcode-cn.com/problems/xuan-zhua...

func minArray(numbers []int) int {
    low := 0
    high := len(numbers) - 1
    for low < high {
        pivot := low + (high - low) / 2
        if numbers[pivot] < numbers[high] {
            high = pivot
        } else if numbers[pivot] > numbers[high] {
            low = pivot + 1
        } else {
            high--
        }
    }
    return numbers[low]
}

面试题9:斐波那契数列

leetcode-cn.com/problems/fei-bo-na...
经典的不能再经典的题,递归的经典使用题型。

func fib(n int) int {
if n == 0 {
        return 0
    }
    if n == 1 {
        return 1
    }
    return fib(n-1)+fib(n-2)
}

这是经典写法,完全按照表达式来实现的,结果在leetcode里面是超过时间限制的提示。体会到网站的强大之处吗?

func fib(n int) int {
    const mod int = 1e9 + 7
    if n < 2 {
        return n
    }
    p, q, r := 0, 0, 1
    for i := 2; i <= n; i++ {
        p = q
        q = r
        r = (p + q) % mod
    }
    return r
}

昨天有人跟我反馈说可以使用尾递归也是一种优化方式,这个确实是,但是不符合leetcode的要求,因为尾递归的方式改变了入参个数。如果要使用,只能添加一个新的函数包裹一层。但是,过不了leetcode的测试用例,我也不知道哪里出问题了,知道的朋友还望告知一下。

func fib(n int64) int64 {
    return getFibNum(0, 1, n)
}
func getFibNum(first, second, n int) int {
    const mod int = 1e9 + 7 // leetcode要求
    if n < 2 {
        return n
    }
    if 2 == n {
        return first + second
    }
    return getFibNum(second, first+second, n-1)%mod
}

面试题10:二进制中1的个数

leetcode-cn.com/problems/er-jin-zh...
我这个道题也是按照人家的答案来实现的,想想大学时光的那些知识点全部忘的一干二净。刚好可以补充一下。

func hammingWeight(num uint32) int {
    var count = 0
    for ; num > 0; {
        count++
        num = (num - 1) & num//与操作,直接得出1的个数
    }
    return count
}

这就是第二章的练习题,也是基础的,如果有不会的,绝对要花时间去学习,研究,尤其是算法这个东西,又没有实际业务支撑的肯定很难学习一下的,全靠自律了,没时间也要挤时间。我问过类似的问题,大佬的回答是:少玩抖音,少玩游戏,时间自然就有了。共勉!!
第三章,研究的时间肯定会很长,因为每一道题都是自己敲会,不会就反复敲,十遍不行就二十遍,知道自己完全会了为止。

本作品采用《CC 协议》,转载必须注明作者和本文链接
欢迎转载分享提意见,一起code
本帖由系统于 2年前 自动加精
棋布
讨论数量: 2
zhaocrazy

不太会呀 :joy: 现在面试都是算法了吗

2年前 评论
棋布 (楼主) 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!