算法复习日:把“似懂非懂”的逻辑磨到通透
原本以为算法复习就是再刷几道题,可坐在电脑前打开之前的笔记才发现,很多算法只是“会写代码”,却没吃透底层逻辑——今天特意放慢节奏,对着二分查找、快速排序这两个“老熟人”死磕,终于把之前模糊的知识点磨得明明白白。
最先攻克的是二分查找的“边界迷思”。之前写代码时,总在“left <= right”和“left < right”之间纠结,有时候能跑通,有时候就陷入死循环,一直没搞懂区别。今天特意找了个有序数组[1,3,5,7,9],分别用两种条件调试:用“left <= right”时,循环结束后left会超过right,适合“查找目标是否存在”;用“left < right”时,循环结束后left等于right,适合“查找目标插入位置”。看着控制台一步步输出left和right的变化,突然懂了:边界条件不是“凭感觉选”,而是由“问题需求”决定的——之前踩的坑,都是因为没把“要找什么”和“边界逻辑”对应起来。
接着复盘快速排序。之前背过“选基准、分区、递归”的口诀,代码也能默写,但一直没明白“为什么选基准要随机化”。今天特意做了对比实验:用固定最左元素当基准,给一个近乎有序的数组排序,耗时居然是随机基准的3倍;查了资料才知道,固定基准在数组有序时会导致递归深度变成O(n),而随机基准能把时间复杂度稳定在O(nlogn)。我赶紧在代码里加上int pivotIndex = left + new Random().nextInt(right - left + 1);,再跑测试用例,效率果然提升不少——原来算法优化不是“炫技”,而是精准解决实际场景中的性能问题。
复习到递归与迭代的转换时,还意外联动了之前学的二叉树。比如把二叉树的深度优先遍历(递归版)改成迭代版,核心就是用栈模拟递归的“调用栈”;而广度优先遍历用队列实现,本质是“先进先出”的顺序控制。看着屏幕上两种遍历的输出结果一致,突然想通了:算法的本质是“逻辑+数据结构”,递归和迭代只是实现方式,核心逻辑都是围绕“如何高效处理数据”展开的——就像二分查找依赖有序数组,快排依赖分区思想,选对数据结构和逻辑,才能写出高效算法。
中途还翻出工程实训时的“图书查询”功能,之前用的是线性查找,遍历整个列表效率很低。今天试着用二分查找优化,先把图书列表按书名排序,再用二分查找匹配关键词,查询速度肉眼可见地变快了。原来算法不是“纸上谈兵”,之前学的知识点真的能直接用到项目里——实训时没优化,就是因为对算法的理解不够深,现在复习完再回头看,优化思路一目了然。
一天复习下来,键盘上敲满了调试代码,笔记本上画满了逻辑流程图。原来算法复习不是“重复刷题”,而是“回头看细节”:搞懂边界条件的由来,理清时间复杂度的优化逻辑,把“会用”变成“懂原理”。那些之前觉得“没必要深究”的细节,恰恰是算法的核心智慧。
晚上打算再练几道“二分查找变种题”,比如查找第一个大于目标值的元素,把今天磨透的边界逻辑再巩固一遍。算法学习就像打磨玉石,只有反复琢磨,才能把“似懂非懂”的模糊点,变成“了然于胸”的通透感~