vlambda博客
学习文章列表

谈谈我是怎么学习二分查找的(学习算法没有捷径)

说点轻松的

大家假期愉快。今天我们不讲知识点,讲一些方法论的东西。

很轻松,希望能对大家有一点点用处。欢迎大家能帮我点点「在看」,谢谢大家。

本期应一位读者评论要求应该安排「滑动窗口」的,但是今天早上突然想起来,想写写二分查找,但是 觉得扯二分查找的细节也没意思,就来说说怎么学习算法与数据结构吧。事实上,「动态规划」我还有一些话没说完,也不可能说得完,以后都安排上。

二分查找很难吗

对,我又来说「二分查找」了。我不知道自己写了多少遍,讲了多少遍。尽管是这样,我还有话说。

我的结论是:

  • 如果解题思路对了,很简单;
  • 如果解题思路不对,就很难!

接下来我会扯一点有的没的,不仅仅是解题。

从二分查找说怎样写代码

关于二分查找,计算机科学家 Donald Knuth 有一句这样的话:

Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky.

很多资料里会引用这位计算机先驱的话,我曾经也被这句话给吓到了,以为二分查找算法是很难掌握的算法。但事实上不是的。去年在北京和一位刷题好友的线下聚餐,他给我们变了个魔术,他说他表演的魔术,就像二分查找之于算法来说就是个入门算法。

我是如何学习二分查找的

我学习二分查找算法的过程就是:做题、学习、调试、总结。

说起来简简单单、没有什么特别的技巧。其实,学习其它的算法和数据结构也一样。一定要建立在一定题量的基础上,我们才可以对一个算法思想和一类算法问题有自己的想法。这个题量是多少,因人而异,因题目本身而异。

遇到问题怎么办

如果没有思路,就去 看看别人的思路和代码,或许解开题目就是一句话,或者只是我们忽略了题目中出现的某个关键字。

如果思路正确,代码不正确,怎么办呢?最好的办法是耐心调试,用一个合适的测试用例,在代码中打印出关键的变量,看到代码执行的细节,是理解程序最有效的办法。

在「力扣」上很多朋友会贴一个代码让我帮他看看错误,这里很感谢这些朋友对我的信任。以前只要是我看到了留言,我都会帮着看一下。现在没时间了,我就把「调试有多有用、多重要」写在了我「力扣」的个人主页中。我帮他们解决问题的办法就是「调试」:

  • 把代码在「力扣」的编辑器里跑一遍,看看哪个测试用例没有过;
  • 然后就把这个错误的测试用例,作为例子,写一个 main 函数放在「力扣」的代码编辑器或者自己的 IDE 中,在程序里加上类似
System.out.println("left = " + left + ", right = " + right + ", mid = " + mid);

的调试语句,就看出问题了。所以遇到一个问题,自己先调试,看到出错的原因,比对着屏幕一直想要有效得多,这也是 liuyubobobo 老师传授给我的经验。

再比如,写二分查找的时候,很多朋友会遇到死循环的问题,解决办法依然是:把造成死循环的用例拿出来,在程序里打上输出语句,看到程序在 leftright 是什么值的时候出现了死循环,然后分析 ifelse 的逻辑就清楚了。

left = 1, right = 4, mid = 2
left = 2, right = 4, mid = 3
left = 3, right = 4, mid = 3
left = 3, right = 4, mid = 3
left = 3, right = 4, mid = 3
left = 3, right = 4, mid = 3

我在「力扣」第 69 题的题解里有具体写那道题是怎么调试的。

我在讲解「回溯算法」为什么在递归完成以后需要把对象类型变量重置的时候,也是用了 在程序中打印关键变量的值 的办法。真的看到了值的变化,才能更好地理解程序。大家在第 46 题、第 39 题的题解中也能看到我通过打印关键变量的值,展示了程序的细节。

调试一点都不难,但是真的很有效。我见过的有经验的工程师基本上不会拿着一段代码自己写的代码去问别人我的代码为什么不正确。他们成长的过程中,很大一部分时间就是在解决自己的问题和别人的问题。在调试的过程中,自己解决问题的能力和对代码的理解也得到了提升。

我如何看待二分查找的模板

「力扣」的「学习」版块给出了二分查找的三个模板,但其实我只用第 1 个和第 2 个。而且我甚至觉得这些模板比较鸡肋,其实它们并不会为我解决一道问题提供有效的帮助。

解决一个问题,重点在思路。

我在写题解和录视频的时候,「思路分析」的部分总会说到:由于题目中给出了什么什么条件,我们想到可以使用什么什么方法。我认为 解题的关键在于审题。并且写了这么多问题下来,二分查找就那么几行代码,也完全不需要记忆,同理可以推广到其它学习算法问题。

希望大家学习下来,也能够融会贯通,举一反三,针对不同的问题有不同的思考,算法思想有相通的地方,但是每道题的具体细节很可能不一样。我们应该把握相同的地方,而不应该用一道题的细节去套另一道题的解法。

总结的作用

总结其实也是刷题的一部分,我总结的方法也比较粗暴,就是 把做题的思路讲清楚,让别人也明白。所以几乎每一道题,我都会花一定篇幅去讲解思路和细节。同时我还会做这样一件事情,就是把我做过的类似的问题作为练习放在题解当中。整理类似的问题的过程就是找到这些问题的共同思想的过程,以后我自己复习的时候能用得上,还能帮助到和我一样想了解这个知识点的朋友们。

今天就说这么多。二分查找听我说,倒不如自己做。下面给自己做一个广告吧。

「力扣」第 4 题(寻找两个正序数组的中位数)官方视频题解是我录的,那道题非常难,当然只要我们掌握了思路,其实是不难的,只是有一些繁琐)。

我花了好长好长时间弄清楚,草稿纸都写了 4 页。并且这道题我录了 2 遍,发布出去的是第 2 版,第 1 版我只单独发给负雪明烛看过。每一遍的制作周期大概是 3 天,我一点一点讲了这道题的分析的过程,并且在代码讲解部分也进行了精心的设计,每一行都讲解了代码是怎么来的。所以很推荐大家看一下,很好找,在中文「力扣」找到第 4 题,第 1 个题解的视频。

这里给自己挖个坑,以后可以和大家讲讲我是怎么录制视频题解的,其实也是一个蛮有意思的话题。

如果觉得我讲得还不错,欢迎大家帮我点点「在看」。感谢大家支持。如果希望我讲什么话题,可以在评论区给我留言呀!再次祝大家假期愉快!