当我们练习算法题时,我们究竟在收获什么?

最近某公司内部出了一则通知, 大意是要求所有在岗的技术人员必须通过内部的一系列技术考试, 内容包括leetcode这类机制的上机编程题。如果规定时间内没通过,则可能被调离到其他岗位。

在寻找方法、攻略并进行练习时,不妨先思考一个问题, 备考的过程除了收获一个证书,我们还应该从中收获什么?

一、提升开发者自测能力

这类考试有一个特点,就是不会给出“错误用例”。
有时也会听到“为什么不能提示错误用例”之类的吐槽。
这种思维类似于"测试为什么没测出来这个bug" 、“缺少堆栈和日志,不能设断点,我没法排查这个问题”

然而被提单次数,会影响自己的代码质量评估。 生产环境往往也因为安全限制,无法直接远程调试。 因此这种思维是很危险和不可取的。
image.png

算法题熟练的同学,有个特点,就是能在接口中提前加上各种判断,或者写上todo注释作为开发遗留。
原因就是在大量的算法题练习过程中,掌握了边界的处理,或者通过阅读代码逻辑,找出其中漏洞。
学习开发者的白盒测试理论是很有利于解决这种情况的。
里面的几种边界,对于开发者自身应该要能熟练掌握。
image.png

因此大家平时如果在leetcode或者其他平台练习题目时,最好以零出错为目目标一次性搞定。 出错的第一时间, 先不要拿着用例去调试, 而是想一想自己为什么会漏了这种情况。 否则很容易形成对错误用例和调试的依赖。

二、加深对编程语言的使用

1.api应用

以java为例, 当你频繁用for循环写各种初始化或者赋值代码时,可能会觉得心很累。
例如需要将"0,1,2,3"这个字符串转成一个整数数组,新手写法是

public static int[] buildIntArray(String str) {
    String[] numStrs = str.split(",");
    int[] result = new int[numStrs.length];
    for (int i = 0;i<result.length;i++) {
        result[i] = Integer.valueOf(numStrs[i]);
    }
}

而使用javaStream的话,一行代码就搞定了

public static int[] buildIntArray(String str) {
    return Arrays.stream(str.split(",")).mapToInt(Integer::valueOf).toArray();
}

因此练习中即使解决了题目,也不妨看看其他人的代码, 进而将好的用法引申到工作中。(当然写太复杂的java stream代码会影响可读性,视情况而用)。

2. 语法问题查缺补漏

另外练习过程中,也能遇到很多编程知识的应用。 假设我们需要自定义一个优先队列,有人写了下面这个代码,却有部分用例无法通过,为什么呢?

PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
    @Override
    public int compare(Integer a, Integer b) {
        if (a != b) {
            // 当a=b的另一种判断条件.....
        } else {
            return  a - b;
        }
    }
});

实际上是因为 Integer包装类型不能用==和!=直接去比较, 因为Integer范围不在-128到127时,比较的就是引用地址而非实际大小了。因此应该用equals方法。从这里可以看出一些编程知识在开发工作中的重要性。

三、收获“思想”,作为程序优化的思想储备

相信很多人从做题攻略和自主练习过程中, 学到了 动态规划、 链表、KMP、二分查找、 并查集等 诸多“套路”。 这些算法却在平时的业务开发中却很少用到, 不免会让人吐槽都是"花拳绣腿",折腾人,对绩效和KPI也没有帮助。

那么可以换个角度理解,学习数学有什么用? 如果仅仅是用于工程应用,不做研究,大部分也只是做api的调用者,不会追究到数学证明、公式推导等层面。
image.png

但数学作为从小到大一直在重点考察的学科一直陪伴着我们。为什么?
个人理解,是因为数学可以锻炼我们的逻辑思维能力,培养一种理性思维和解决问题的方式。

算法和数学都是一种思想。 包括你去学习设计模式、学习jvm原理、学习操作系统等,都是在学习一种思想。 而对思想的理解和加深,则需要大量的练习。因此上机编程成了重要的考察方式。

以二分查找为例,有的人只关注循环不变式模板、"逃课"api(指不用手写二分,可以直接用的接口,例如treeMap的floor())
有的人则会在大量的练习中,悟到什么时候、哪种情况下可以剔除不必要的搜索遍历过程,并使用二分解决性能问题。

因此建议大家在备考刷题的过程中, 除了关注"套路"、"题目面板"外,多关注一下这个解法的背后思想是什么, 当你能理解到这一个层次时, 往往对你的编程工作会有持续的潜移默化影响,尽管可能不是那么显而易见罢了。

四、保持代码思维和手感

大家应该都有这种时期, 就是某段时间都是各种杂事,可能1个月就写了几百行代码,事后再捡起来就不会写了。
因此这种时期练练题目,对自己也维持手感也会有帮助。有句话叫做“ 流水不腐户枢不蠹”。
另外看到一段关于跑步的话,这里分享一下:

如果你经常跑步,你也许会知道其实 4 个多小时一点都不快,完全就是业余跑者的水平;也许你也知道,其实即便是大众跑者,5 年完成百公里也是相当难的,甚至身体的原因,不是所有人都能跑下来越野。
所以当我开始追求速度时,慢慢发现,当我在为 5 公里没有跑进 20 分而焦虑和努力时,学校里的那些体育生都在追求如何跑进 17 分了。后来我释然了:之前我对于跑步的疯狂,是因为没有搞懂跑步的意义:
因为跑步不会让我毕业。
因为跑步不会让我找到工作。
因为跑步不会让我找到女朋友。
跑步对于我的意义,就是能让我保持身体健康,仅此而已。


以上就是我的一些想法,个人技术水平和经验有限,没有资格让大家全部认同。
另外考查程序员素养的指标绝对不仅仅只是代码。上机题的考察方式本身也可能存在缺陷。如果你是对此有更深理解的朋友,也可以讲一下你对这方面的看法和收获,大家一起学习和参考。

(完)