前言:

昨天我没有继续学新语法,而是把时间都花在练题上。在老师的指导下,我完成了力扣(LeetCode)数组相关的 4、11、14、15、46 这五道题。从读题、讲思路,到动手写代码、再复盘,整个过程让我觉得比单纯看教程收获大得多。

一、练题的第一步:先学会读题

刚开始拿到题目,我的第一反应就是“看不懂、没有思路”。老师告诉我,拿到一道题不要急着写代码,先做三件事:

  1. 用自己的话把题目复述一遍;
  2. 确定输入是什么、输出是什么;
  3. 先想一个最笨的暴力解法,再去想怎么优化。

这样做之后,我发现很多题目其实并没有想象中那么难,难的是我一开始根本没读懂题目在问什么。

二、五道题的核心思路速览

  • 第 4 题:两个正序数组的中位数。最简单的思路是把两个数组合并后取中间值,但题目要求更高效的二分做法。合并版本我独立写出来了,二分版本是在老师一步步讲解下完成的,目前只理解了整体框架。
  • 第 11 题:盛最多水的容器。用左右两个指针从两端向中间移动,哪边高度矮,哪边就往中间走,因为只有移动较矮的一边才可能让面积变大。这道题让我第一次感受到“双指针”的巧妙。
  • 第 14 题:最长公共前缀。先把第一个字符串当作基准,再拿它和后面的字符串逐位比较,遇到不一致就截断,剩下的就是公共前缀。
  • 第 15 题:三数之和。先排序,再用双指针寻找另外两个数。这道题最麻烦的是去重,我在这里写错过好几次。
  • 第 46 题:全排列。用回溯法:选一个数加入当前路径,递归处理剩下的数,返回后再撤销刚才的选择。

三、印象最深的两道题

第 15 题三数之和让我明白,光有思路不够,细节决定成败:排序后左右指针怎么移动、什么时候跳过重复元素,稍不注意就会得到重复答案。

第 46 题全排列是我第一次接触回溯,代码不长,但“选择 -> 递归 -> 撤销选择”这个过程让我想了好久。看懂之后自己再敲一遍,终于理解了为什么递归调用后要 path.pop()

演示代码(以第 11 题为例):

def max_area(height):
    left, right = 0, len(height) - 1
    ans = 0
    while left < right:
        area = min(height[left], height[right]) * (right - left)
        ans = max(ans, area)
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1
    return ans

print(max_area([1, 8, 6, 2, 5, 4, 8, 3, 7]))

运行结果:49

四、写完之后还要复盘

老师要求我每道题做完后做三件事:先把思路口述一遍,再看代码里有没有重复和冗余,最后分析时间复杂度。以前我觉得“能跑通就是胜利”,现在我会主动问自己:这个解法为什么是对的?还有没有更好的办法?

结尾留言:

昨天的练题让我认识到,Python 语法只是工具,真正难的是把判断、循环、列表、递归这些知识组合起来解决实际问题。接下来我会继续练题,把每一道做过的题都整理成自己能讲清楚的笔记。

更多推荐