纵横算法之四:算法应该怎么学

我们继续聊算法,现在要谈一个最重要的问题:算法应该怎么学!算法的脉络是什么?怎么才算学透,要做多少题?要花多少时间?为什么总感觉很多教程啥的没有用?

先说结论,目前公认最好的方式是 按照主题学习,循序渐进。说人话就是按照小专题刷,一次学习一片题目。按照LeetCode的顺序刷的坏处是很多相邻的题目涉及的知识并不一样, 有的题考察二叉树,有的是链表,有的是递归,有的是数组,前后不搭边的问题使你无法将一个知识点或者技能训练到熟练的程度,就容易出现刷的越多,脑子越乱的问题,也许走火入魔就是这么回事吧。

其实任何一门技术,不管你是唱歌跳舞还是历史地理,学习的时候都会按照主题的方式来推进,因为这样才能看透本质、融会贯通,真正解决你的问题。对于算法里的数组,链表,二叉树等等,每个都梳理清楚其基本问题是什么,有哪些常见的小专题,每个小专题有哪些典型的题目,每种题目的典型特征和注意事项是什么。这样你会发现每个小专题你精研几个题就够了,剩下的很多题目根本就不用做,然后精做150多道题就能征服面试80%以上的题目,这不就是是事半功倍吗?

具体来说按照主题的好处是:一招多提,看透本质;一题多解,融会贯通。下面我们具体看一下。

1.一招多题,看透本质

我们先看一个面试题:

给定一个二叉树,返回其节点值自底向上的层序遍历。(即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历),例如下图:

  输出结果为:

[
  [15,7],
  [9,20],
  [3]
]

看到这个题你是否有思路呢?如果研究二叉树比较少,可能半天也想不到该怎么做。如果再给你一些时间,可能想到深度优先,但是顺序好像和预期的不一样。可能过了大半天想到可能用广度优先更好一些,那该怎么层次遍历呢,怎么才能倒着来呢?一堆的疑惑,貌似挺难的,对不。

我用这个题目测试了一些小伙伴,大部分人都没啥思路,只给我回复了一个惆怅的表情。而在咕泡的集训班里, 我看到太多人遇到这种题目,直接走人了,这个题真有这么难吗?

如果学过数据结构,一定记得树有个层次遍历的方式,就是先将根节点的所有子节点都先访问了,然后再依次访问下一层。每一层的节点先用一个队列保存,某个元素出队的时候随便将子节点都入队,这样一层访问完的时候下一层的节点也恰好都入队了。这就是层次遍历的基本过程。

上面这么做,虽然能够按照层次访问所有的元素,但是在很多场景下需要将每层的元素区分开来。这里可以通过一个变量等方式来实现。拿到一层层的元素之后呢?我们可以做很多事情:

1.求每一层的最大值

2.求每一层的平均值

3.将奇数层显示逻辑不变,偶数层的元素反转,也就是之字型访问

4.只打印每层最右侧的元素(右视图)

5.按照从最底层到根节点,倒序打印

6.将二叉树改成N叉树,也是层次输出

7.只打印每层最左侧的元素

8.找每层最小的元素

9.只打印每层最左侧的元素(左视图)

10.请找出该二叉树的 最底层 最左边 节点的值。

11.请找出该二叉树的 最底层 最右边 节点的值。

是不是只要能准确获取每层的元素数量,这么多情况都轻轻松松搞定?那问题就是该如何获取每层的元素呢?这个就是你再学习二叉树时必须解决的,在我的算法课中我会花一半的时间来讲解清楚如何分层,然后只用半小时来将这11道题,最快的时候4分钟就讲完了3道题,为什么呢?因为你已经掌握层次遍历的体系了,后面只要改一下条件就可以实现这些功能了。这就像爬树一样,当你爬到树顶了 ,在多个枝上摘桃子还不是轻而易举的事。

你可能会问了,这11种情况搞来搞去有啥实际意义吗?其实没有啥用,工作中不太可能遇到,但是这些问题正好对应了LeetCode的515题、637题、103题、199题、107题、429和513题。看到了吗,学会一个技术,我们可以一下子解决这么多题,而且也更加牢固。有时候会看到某个人说自己一晚上做了20道题,或者一周刷了一百多道题,现在你还会惊讶吗?

按照主题刷除了节省时间,学得更牢外,还有个好处,让你看到出题的规律,上面无非就是拿到每层元素之后,找一个情况就造一道题,而上面这11道题中,8、9、11就是我造的题,不是LeetCode里的。

你会觉得,这应该是个例吧,你怎么知道其他题目也是这样的。好!我们再看个例子:

给你一个链表,每 3 个节点一组进行翻转,请你返回翻转后的链表,不足一组的不做处理。例如开始的链表为:

1->2->3->4->5->6->7
三个一组,则结果为:
3->2->1->6->5->4->7

这个题目会怎么做?你可能想到先遍历 ,然后将找到的组分别反转。是的,就是这么个思路,但是当你开始写的时候发现贼难处理,如果反转?首结点怎么处理?中间怎么交换?组和组之间怎么处理?一堆的问题,结果写着写着就发现写不下去了。

事实上,在链表的练习中我们会练习如何在不申请额外空间的前提下实现链表反转,之后我们还可以继续训练如何在链表中将指定的区间进行反转, 还会训练如何实现两两反转,最后我们还会训练如何将k个一组进行反转。如果经历过这样一个过程,我们就会发现此时再让我们做三个一组反转的是不是就很容易了?

你可能会问,我怎么知道要训练上面几种方式呢?上面对应的就是LeetCode25、Leetcode24等题目,特别是LeetCode25,就是k个一组反转,如果这个练习好了,直接将k换成3不就行了吗?

这种例子特别特别多,至少有2/3的常考题都属于这种情况。

那链表反转到这一步就够了吗?不是!还有问题,再看一个:

假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。

给定两个这种链表,请生成代表两个整数相加值的结果链表。
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。

这个题,只看题意貌似和链表反转没关系是吧?但是仔细分析你会发现,链表访问顺序是937,而我们做运算的顺序是739,因为会有进位的问题,所以我们必须先将两个链表反转,加完之后变成 0 0 0 1,然后再反转过来变成1000。当然用栈也可以,这个后面再说。

如果对反转没有研究,这个题如果面试直接问,很可能直接挂掉。假如将加法换成减法呢?要借位,难度就更大了,而这个题就是我曾经面快手遇到的,很可惜当时功夫还不够,没写出来。像这种问题就是我们在平时要好好练的,假如面试的时候将加减换成乘法或者与或操作,是不是就容易很多了?所以我们刷题不是为了做题而做题,找到一些内在关系,一次掌握一个技术链条才是重要的。

2.为什么总感觉很多课程啥的学了没用

之前在学习算法时,我总感觉很多课程是讲了很多内容,但是总感觉面试的时候用不上,感觉距离面试的要求差老远了,为啥会这样呢?

我们继续看反转题目之间的关系

 图中第一列是所有的教程都会发大量篇幅介绍的问题,有些会再放一个指定区间反转或者K个一组反转,而面试的难题可能会遇到两两反转、链表加法以及其他更多的类型。很明显可以看到一般的教程在第一个红圈部分大讲特讲,但是涉及第二个红圈的内容非常少,所以并没有将这个技术链条帮你打通。任督二脉不通,关键时刻自然顶不住。

这样的链条有几个呢?整个算法里大约有二三十条吧,什么时候掌握了,什么时候算法你就学到家了。 

3.一题多解,融会贯通

我们常说,学习要融会贯通,但是怎么才能融会贯通呢?

我们先来看一个高频考题LeetCode234题,我曾经面京东的时候也写过。

给你一个单链表,判断这个链表是否为回文链表。

例如如果一个链表是 1->2->2->1,就是回文链表。如果是1->2->3->1,就不是回文链表。

这个题你看到之后能想到几种方式来处理?我能想到6种方法。不信来看:

(1)将链表元素赋值到数组,然后用右标从两端到中间来判断。

(2)将元素全部压栈,然后一边出栈,一边与原始链表比较,如果都一致,就是回文链表。

(3)改造上面的方法,先遍历一遍获得长度,然后只将一半的元素压栈。之后的一半元素一边遍历一边与出栈的元素比较。如果都相等,就是回文链表。

(4)采用反转链表的方式,先创建一个原始链表的反转链表,之后再分别遍历两个链表来对比,都一样,则为回文链表。

(5)对(4)进行优化,先遍历一遍,找到中间元素。只为一半的元素创建反转链表,之后两个链表一边遍历,一边比较,都一样则为回文链表。

(6)还是对(4)找中间位置的过程进行优化,使用快慢指针,fast一次走两步,slow一次走一步,当fast到达表尾的时候,show正好在链表的中间,接下来就可以逆序前一半或者后一半。

看到了吗。一个题我们能想到很多种方法处理。那这个是不是个例呢?我们再看一个题:输入两个链表,判断他们是否有公共结点,有则找出第一个公共节点。例如下面这样子:

这个题该如何入手呢?好像一时想不到是吗?我告诉你这个题至少有4种可行的方法。 首先是蛮力法,类似于冒泡排序的方式,将第一个链表中的每一个结点依次与第二个链表的进行比较,当出现相等的结点指针时,即为相交结点,但是这种方法时间复杂度高,而且有可能只是部分匹配上,所以还有要处理复杂的情况。排除!

第0种:数组法:

第一种是Hash法,将第一个链表全部保存到Hash中,然后遍历第二个并检查是否在Hash中存在,有的话一定能找到,OK,第一种方法。

第二种是集合呢?集合的底层实现就是Hash,可以和Hash一样用,OK,第二种方法。

第三种:栈能解决问题,首先创建两个栈,将两个链表分别入栈,然后再一边出栈,一边比较,如果存在公共结点 ,自然就找到了。于是就有了第三种方法。

如果能回到到这一步,其实算法面试就算过关了,下面这种方法比较难想到,需要我们练的时候学习。

第四种,如果我们将两个链表分别拼接成AB和BA,就会发现:

AB:0-1-2-3-4-5-a-b-4-5

BA:a-b-4-5-0-1-2-3-4-5

此时分别遍历两个合并后的链表,走到第二个4的位置之后,就是 4 和5,正好就是重合的位置。

看到了吗?一个题目我们可以有很多种方式解决。

你可能会问了,有一种方法不就行了,为啥要搞这么多方法?

注意上面两个题,你会发现我们都从链表、栈、双指针等不同的角度来思考,都能解决问题,只是解决问题的难易程度和需要的空间不一样。这就意味着,我们 每做一道题,就将基本功练习了好几遍。这有什么好处呢?是为了融会贯通!

面试官给了你一个算法,如果一时想不到该怎么办呢?连解决问题的思路都没有,这是很多人倒下的第一步。

这时候我们可以将常用数据结构和常用算法都想一遍,看看哪些能解决问题。

我们先在脑子里快速过一下谁有可能解决问题。比如上面的题,我们快速想队列行不行,栈行不行,双指针行不行,等等。如果没想清楚,你也可以和面试官,可能用某某方法可以。接着他会问你怎么做,你可以继续思考,面试官也会给你几分钟让你思考,如果你最后发现不行, 则可以直接说这种方式不行,该用什么什么方法。直到最后找到最合适的方式。

上面这个过程,我们一般不会注意到,但是这就是人的思维方式。常用的数据结构也就数组、链表、队、栈、Hash、集合、树、位图、堆等。常用的算法思想也就查找、排序、双指针、递归、迭代、分治、贪心、回溯和动态规划等等。

虽然LeetCode已经有接近2000题了,但是大家常接触的不到两百道。而这些高频题使用的具体方法也不过上面这个范围的。我们虽然说一题多解,但是就是在不断练习这些基本数据结构和算法的,根据要求进行组合与调整,这才是刷算法真正该训练的地方。毫无目的的乱刷,只会越刷越迷茫?如果我们平时刷算法将这些内容刷的滚瓜烂熟 ,遇到各种基本操作信手拈来,面试怎么会过不了?

# 4.准备算法需要多少时间

 这个是问我最多的问题,我的回答是不知道。不知道你准多大决心来解决算法,不知道会花多少时间来学习算法,也不知道你会用多少时间来找到这二三十条技术链,也不知道每个技术链你是否清晰,花多少时间揣摩透,所以我不知道。

我现在在咕泡开设了一个算法集训班,当前还是初级算法为主,高级部门要到2022年春节之后再开始了,这个课程会提供完整的讲义帮你的也只是理清楚技术链和每个链的内容,另外提供录播来降低学习的压力,还有直播答疑来及时解决你的问题,但是功夫在平常,没有大量的练习和思考仍然是不够的。

课程地址:咕泡云课堂

那想学算法,但是时间不够怎么办呢?下一篇我们继续聊时间的问题!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纵横千里,捭阖四方

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值