
写这个系列的时候我给命名栏填了“更弱智的算法学习”。起名那会儿是day03刚被一道滑动窗口卡了三个小时心态属实有点崩。按理说叫“算法学习日记”更体面但我知道自己几斤几两真正能把一道题的暴力解法改成AC解能把KMP的next数组一次写对能在不看题解的情况下想到二分答案——这些“正常人标配”的能力我得花很多天才能勉强摸到门槛。所以“更弱智”不是自谦是阶段性事实判断。不过这32天走下来我也确实攒了不少东西。这系列本来只是写给自己的复盘笔记结果后台看到不少人顺着“算法”相关关键词摸进来有准备蓝桥杯的学生有半路转行投算法工程师简历的也有做机器人、做控制的老哥在找路径规划、PID参数整定的资料。热搜词列表里那些东西从KMP、归并排序到粒子群、DQN、PPO跨度大得离谱。但仔细想想这恰好代表了算法学习者真正会遇上的三类问题面试要考的、工程要用的、论文里看到的。名字是自嘲内容我却想认真写。这篇文章是day32的阶段性总结我把它整理成能直接“抄作业”的版本希望能帮到正在这条路上挣扎的人。1. 从“刷题打卡”到“算法认知”学了几十天我彻底想明白的事先说个扎心的事实我前十天基本是在假装学算法。每天打开题单看到一道题标记“不会”然后点开题解背下思路觉得自己又进步了。这种状态特别像背单词只背到abandon——你会越来越多的算法名词但遇到新题照样懵逼。直到有一天我做一道“合并两个有序链表”明明背过归并的套路写出来的代码却连边界都处理不对我才意识到问题出在哪儿。1.1 第一阶段见一个记一个假装在学算法这个阶段最典型的症状就是“收藏即学会”。看到堆排序收藏看到KMP收藏看到Tarjan收藏。浏览器书签几百个脑子里一个都没有。我一度以为背下“建堆O(n)下滤O(logn)”就懂堆排序了结果让我手写一个建堆过程我愣是写出了插入排序的翻版——元素确实有序了但堆结构完全不对。其实算法学习里最难的不是“理解”是“输出”。你盯着一个算法的代码看三遍会觉得丝滑顺畅但等你自己关上答案写第一行就卡住。我后来特意做了个“复现训练”每天学一个新算法先读题解合上屏幕用纸笔把流程画出来再凭记忆写一遍代码最后对着标准实现逐行找差异。差异点就是你的认知盲区。这样练了五六天效果比干看题解强得多。1.2 第二阶段从“名字”走向“复杂度与数据结构”背多了开始发现算法的名字其实是最不重要的东西。真正决定一个算法能不能用、贵不贵的是两个维度时间复杂度和空间复杂度。更准确地说是“最坏情况下的复杂度”和“实际运行中的常数因子”。这两件事在教科书里写得轻描淡写但工程里天天都在纠结。举几个我踩过的实例。冒泡排序和插入排序的最坏复杂度都是O(n^2)但插入排序在近乎有序的数据上能跑到O(n)冒泡却不能。快排平均复杂度O(nlogn)最坏却退化到O(n^2)所以工程上才会有“三数取中”和“随机化基准”的改良版。堆排序号称O(nlogn)且原地排序但因为它的实际常数比快排大大多数语言的sort函数反而选的是快排或Timsort。这些细节教科书不一定讲但面试和实战都会问到。1.3 第三阶段场景驱动算法背后是问题的结构差不多到day20我才摸到一点门道学算法的正确姿势不是“学算法”而是“学问题结构”。暴力枚举适合数据量小的场景因为n很小的时候O(n^3)也没什么分治和二分解决的是“有单调性/可以拆半”的问题动态规划解决的是“有重叠子问题和最优子结构”的问题图论算法解决的是“实体之间有连接关系”的问题。想通这一点之后我再看到一个题目或需求第一反应不再是“这题用哪个算法”而是“这个问题的结构属于哪种”。排序搜索最优化约束满足路径规划状态转移结构定了候选算法范围就缩到两三个剩下的就是查资料加上手试。这个过程让我对热搜词里那些看起来毫不相关的东西有了统一的理解——KMP、匈牙利、粒子群、PPO本质都是在给定约束下寻找某种最优或可接受的解区别只在于问题的结构和约束的类型。2. 字符串、排序与图论把经典算法学出深度经典算法专题是面试和竞赛的地基也是热搜词里出现最密集的范围KMP、冒泡排序、堆排序、归并排序、Tarjan、匈牙利全在这里。这个专题我的建议是不追求数量追求“能自己推导”。你如果能白手起家推导出KMP的next数组计算逻辑能徒手解释堆排序为什么不稳定能说明白Tarjan为什么只要一遍DFS那你在这个专题上的水平就已经超过大部分背题选手了。2.1 KMP能自己写出next数组才算真正入门KMP是字符串匹配算法里最经典的入门题。朴素匹配的复杂度是O(n*m)主串一个位置一个位置试每次失配只右移一位。KMP的核心突破是主串指针i永不回头模式串指针j根据next数组回退从而把匹配过程摊平成O(nm)。我第一次看KMP的图解觉得懂了——next数组嘛就是“最长相等前后缀的长度”。结果自己写代码时在“为什么失配时要j next[j-1]”这一步卡了一整天。后来我换了个视角才真正想明白next数组的本质是模式串的“自我匹配”。你在计算next[i]时做的是“模式串的前缀匹配自己”这和你用模式串去匹配主串是一个道理。所以next的推导本身也要用KMP的思想去加速这就是所谓“前缀函数”。下面是标准前缀函数实现注释标出了我当时看不懂、后来才理解的关键位置vectorint buildNext(const string pat) { int m pat.size(); vectorint next(m, 0); for (int i 1, j 0; i m; i) { while (j 0 pat[i] ! pat[j]) { j next[j - 1]; // 失配时j回退到更短的相等前后缀 } if (pat[i] pat[j]) { j; } next[i] j; } return next; }我建议大家不要背这个代码而是拿pat ababc手动跑一遍。你会在i4的时候发现j先到2然后因为pat[4] c不等于pat[2] a回退到next[1] 0。这一跳就是KMP的“聪明”所在。理解了这一跳你就理解了整个KMP。2.2 排序算法稳定性和复杂度的直觉排序算法是热搜词里的常客从冒泡到归并、堆排序、C STL里的sort实现全都是高频话题。我按自己的学习顺序把它们串起来讲。冒泡排序适合入门理解“比较-交换”模型但实际工程基本不用。归并排序的价值在于它的稳定性——相同元素的相对顺序不会变而且最坏情况稳定在O(nlogn)代价是需要O(n)的额外空间。堆排序的价值在于“原地”和“最坏O(nlogn)”但它的不稳定和较差常数让它被很多排序场景排除。快排的平均性能最好所以成为绝大多数标准库的首选但它最坏情况退化的问题必须在实现上做文章。很多人问为什么堆排序不稳定我用一个简单的例子说明。假设待排序列里有两条记录键都是3一条在前一条在后。堆调整过程中父节点和子节点会交换位置完全可能把后面的3换到前面来有序性就破坏了。这不是“堆排序实现错了”而是堆这种数据结构天然难以保证稳定性。面试里问到这题如果你能答出这个层面面试官一般会满意。2.3 图论三兄弟Tarjan、匈牙利和“问题归约”图论这块是热搜词里容易被忽略但分量极重的部分。Tarjan算法是求强连通分量的经典算法它只用一遍DFS配合dfn时间戳和low数组就能把图里的强连通分量全找出来。它的核心思想是DFS过程中维护一个栈当某个节点的dfn等于low时说明栈顶到它这一段就构成一个强连通分量。这种“用递归过程本身记录结构”的思路比任何记忆模板都重要。匈牙利算法解决的是二分图最大匹配问题。它的核心是增广路径如果能找到一条起点和终点都是非匹配点的路径路径上匹配边和非匹配边交替出现则取反这条路径就能让匹配数加一。这个思想在很多“配对分配”场景里都能用。我学到这里最大的体会是“问题归约”很多看似无关的问题底层结构是同一套。比如把任务分配给工人让总成本最小可以归约成带权二分图的最小权完美匹配问题把一堆相互依赖的任务排序并找出关键路径会用到拓扑排序和DP。算法学习的快感其实就来自这种“原来你也是图论问题”的瞬间。3. 工程派算法路径规划、PID、滤波与校验到了这个章节搜索引擎的热搜词画风突变——A*算法、DWA、PID、Mahony、HALCON滤波、3DES、完整性校验算法全冒出来了。这也是我想写这篇总结的重要原因算法不只是LeetCode上的题还是机器人、无人机、工业相机、嵌入式设备里每天在跑的代码。这些“工程算法”往往不被竞赛党注意但真实岗位里天天在用。3.1 A*与DWA机器人怎么找到路又怎么躲开障碍A算法是我个人最偏爱的路径规划算法没有之一。它的核心公式就一个f(n) g(n) h(n)。g是从起点到当前节点的实际代价h是当前节点到终点的启发式估计代价。A每次选择f值最小的节点来扩展所以它既像广度优先那样“稳妥”又像贪心那样“有方向感”。这里有个关键数学性质只有当h不超过实际最小代价时A*才能保证找到最优路径这个性质叫“可采纳性”。工程上常用欧几里得距离或曼哈顿距离做h它们都是可采纳的。如果你是做游戏寻路或者机器人导航这几乎是必考题。DWA动态窗口法则是另一类问题——局部规划也就是“大方向知道了怎么在接下来几秒内避开突然出现的障碍物”。DWA的思路非常工程化在当前速度空间里采样一堆候选速度组合每个组合模拟出一条几秒内的轨迹再用评价函数打分离目标近、离障碍远、速度合适选最高分的速度执行。这个过程每帧重复形成滚动优化。我第一次看到DWA的示意图时想这不就是“暴力枚举候选摇杆位置”嘛但配合上机器人运动学模型它还真能稳定地穿过密集障碍区——工程的美感往往藏在朴素的枚举里。3.2 PID与Mahony控制与姿态里的“老黄牛”PID是自动控制领域最经典、也最被低估的算法。P是比例见误差就反应I是积分专门消除长期存在的稳态误差D是微分提前感知误差变化趋势压制超调。工程调试PID的常规顺序是先把I和D设成0只调P直到系统临界震荡然后加一点D压住超调最后加一点I消残差。这个过程看起来很简单但真到现场会发现每个参数之间互相影响而且系统的延迟还会让你调的增益“虚高”。Mahony算法是姿态解算里的互补滤波方案。无人机、机器人身上都有加速度计和陀螺仪。陀螺仪短时间内很准但积分久了会漂移加速度计不漂移但容易受震动干扰。Mahony的思想是把两者的优点互补用加速度计的输出去修正陀螺仪的漂移得到一个长期稳定又不抖动的姿态估计。虽然现在很多产品已经用更复杂的卡尔曼滤波或动捕方案但Mahony这种“取长补短”的思路在理解传感器融合时依然非常值得掌握。3.3 滤波与校验算法工程现场的真实需求热搜词里出现的“HALCON滤波算法”“完整性校验算法”“3DES 双倍长 解密算法”看着偏门其实反映了工业现场的真实需求。机器视觉里图像噪声需要滤波来抑制——均值滤波、中值滤波、高斯滤波是三个最基础的后面还有基于边缘保持的双边滤波。不要小看这些经典滤波工业相机的打光环境千奇百怪选错滤波kernel后续的特征提取和模板匹配全都受影响。完整性校验算法则更基础为了让数据在传输或存储过程中不被篡改需要给数据算一个“指纹”比如MD5、SHA系列或CRC。校验算法和加密算法是两回事校验关心“数据有没有被改”加密关心“数据明文不能被看到”。我之前在嵌入式项目里见过拆板出来的EEPROM里配置了完整性校验就是为了防止配置被意外改写这类需求在飞控、存储、固件升级场景里非常多见。看到热搜里有人搜相关设备型号我猜他们大概率也在做类似的东西——无论如何先把CRC和哈希的差异理清楚是这一类需求的第一课。4. 强化学习与群体智能从DQN到MADDPG/MAPPO说句实话强化学习这块我day32也还只是“入门偏上”的水平。但热搜词里DQN、PPO、MADDPG、MAPPO、粒子群扎堆出现说明想学的人很多。我把自己从零啃下来的经历写在下面重点讲清楚“为什么这样设计”而不是堆公式。4.1 DQN核心为什么需要target network和replay bufferDQN是深度强化学习的入门第一课。它解决的问题是当状态空间太大、没法用表格存Q值时用深度神经网络去逼近Q函数。但如果你直接拿神经网络在线学会发现训练非常不稳定。原因有两个一是连续采样的样本之间高度相关网络会被新鲜样本带偏二是更新目标本身也在用自己刚更新的参数计算网络会“追着自己的尾巴跑”。DQN的两个核心组件就是冲着这两个问题去的。经验回放replay buffer把过去的经历存起来每次训练随机抽一批切断样本之间的时序相关性同时样本还能复用好几次目标网络target network用一个滞后更新的副本去计算目标Q值让网络在更新时不至于同时移动“靶子”和“箭”。我在MATLAB里按《深度强化学习算法》的框架复现了一次DQN调参过程中发现replay buffer容量太小、batch size太大、target网络更新频率太高都会让训练曲线像心电图一样跳动。这俩设计不是我发明的是无数实验试出来的血泪经验。4.2 PPO、MADDPG、MAPPO从单智能体到多智能体PPO是策略梯度家族里现在最常被使用的算法。它解决的核心问题是“策略更新步子迈多大”。步子太大可能一步就把策略踢到性能悬崖步子太小更新太慢。PPO用重要性采样加裁剪的方式限制每次更新的幅度让训练稳定很多。我个人的体会是理解了“裁剪”这个看似简单的操作就理解了PPO为什么能成为社区默认选择。多智能体方向MADDPG和MAPPO是目前最热门的两类baseline。MADDPG的思路是每个智能体在训练时都有一个“全局裁判”——它的critic能看到所有智能体的动作和状态这样每个智能体的策略在训练时就能充分考虑队友的行为缓解环境非平稳性问题。MAPPO则是把PPO的多智能体版本共享参数、集中训练、分布执行。做多智能体仿真时我建议先跑MAPPO因为它的收敛稳定性通常比MADDPG更好调。这里我踩过不少坑比如reward sana的设计权重、action space的scale都会极大影响最终效果。4.3 粒子群与群体智能粒子群优化PSO跟深度学习、强化学习不太一样它属于“无梯度优化”的群体智能方法。思路非常朴素初始化一群随机粒子每个粒子记录自己历史上最好位置pbest和群体历史最好位置gbest每次迭代按一定权重飞向这两个位置附近同时保留一定随机性。这个过程就像一群鸟找食物每只鸟既参考自己的经验又参考群体的发现。PSO的优势是简单、好实现、不用求导适合目标函数复杂、没有梯度信息的优化问题。我在做参数搜索时就经常用PSO去找PID参数或者DWA评价函数的权重效果往往比网格搜索快很多。它的缺点是容易陷入局部最优不过在问题不太复杂时它已经足够好用而且是理解“探索与利用”平衡最好的入门算法之一。5. 算法面试与竞赛刷题知道自己为什么要学热搜词里的“算法工程师面试”“java 蓝桥杯算法题目”“leecode必刷基础算法题”说明很多人学算法是为了应付面试或比赛。这本身没什么问题但我想说的是刷题和面试是两回事比赛的技巧和工程的能力也不完全重合。想清楚自己为什么学才不会在错误的细节上浪费大量时间。5.1 蓝桥杯和LeetCode两种练习场景的定位蓝桥杯这类竞赛考察的是在规定时间内用有限资源解决一系列“有套路”的问题。它更看重你算法模板的熟练度、数学建模能力和代码速度。很多题都有固定的解题套路比如数论里的模运算、图论里的最短路、DP里的背包九讲。临近比赛前一天一套真题比漫无目的刷难题效率高得多。LeetCode则是面试准备的主流平台但它更偏数据结构基础和通用问题模式比如哈希表、双指针、前缀和、二分、单调栈。我建议非竞赛选手的日常练习以LeetCode为主但不追求“一天刷十题”而是“一天吃透一题”。一道题能讲清楚时间复杂度的推导过程能写出空间O(1)的进阶版能说出和它同类的两三道题的关联比AC一百道更有效。5.2 面试官到底在考察什么算法工程师面试往往不只是算法题。我复盘了最近几次面试经历发现面试官真正想确认的有四件事第一你能不能把模糊需求转化成明确的问题第二你会不会分析复杂度并做取舍第三你的代码是否干净、健壮边界条件有没有处理好第四你懂不懂算法背后的原理而不是背模板。举个例子面试官问你“怎么设计一个限流器”可以聊内存算法令牌桶、分布式方案RedisLua、甚至聊到“如何让多个服务共享限流状态”。这题没有标准解完全是看你的知识面和工程直觉。如果平常只盯LeetCode这类问题大概率会暴露短板。5.3 一个可复制的30天学习路线day33之后怎么走我这32天走了不少弯路如果把路线重走一遍我会这样排day33-40动态规划专题按“背包-线性DP-区间DP-树形DP”顺序刷每类题至少独立写三遍。day41-45图论专题重点搞懂最短路、最小生成树和拓扑排序能独立推导Dijkstra、Floyd和Kruskal。day46-50字符串算法把KMP、字典树、后缀数组的“用途”和“大致实现”搞清楚不需要背板但要能说出复杂度。day51-55工程算法自己动手写一个A*寻路、一个PID控制器、一个DWA采样器不强求效果完美重点是理解“参数影响行为”这个闭环。day56-60强化学习入门用现成框架跑通DQN和PPO能手绘出它们的网络结构和数据流图。这条路线不一定适合所有人但它是我复盘后最想推荐给自己day01的路线。6. 踩坑记录与问题速查最后这部分是老规矩把我的各种踩坑经历整理成“速查表”方便你日后翻出来对号入座。6.1 我踩过的几个大坑第一个大坑是“只输入不输出”。连续看视频、刷题解感觉自己一直在学习其实是在消费知识。后来我强迫自己每天写一篇笔记哪怕只有几百字也要把算法流程画出来。这习惯一养成学习密度立刻不同。第二个大坑是“手写算法时不够依赖复杂度分析”。我经常写完一个AC代码就收工完全不管它是不是最优。后来面试官问我“这两个版本哪个更好”我才意识到自己从来没想到在时间空间之间权衡。algorithm的学习复杂度就是“鲁棒性”的地基地基不稳上层再好看也没用。第三个大坑是“把强化学习当黑盒调参”。早期我跑PPO训练曲线掉了就改lr改了没用就改clip最后全凭玄学。后来认真读了PPO的paper和几份开源实现才明白reward的尺度、优势估计的计算方式、batch组成都会极大影响稳定性。强化学习的调试本质上是“假设-实验-验证”的科学过程绝不是盲目的网格搜索。6.2 一句话速查表场景推荐方向一句话避坑小规模数据暴力遍历枚举、递归n超过20先想剪枝字符串匹配KMP、字典树KMP的next别背按“前缀函数”理解大文件排序归并排序、外排稳定性要求高时别用堆排序网格地图寻路A*、D* Liteh函数必须可采纳否则不是最优路径移动机器人局部避障DWA、TEB一定要考虑机器人运动学约束电机/无人机控制PID、Mahony先调P再调D最后加点I别三管齐下图像降噪高斯、中值、双边选kernel前先分析噪声类型数据完整性校验CRC、SHA校验和加密是两码事连续控制强化学习PPO、SAC先看reward scale再谈调参多智能体协作MAPPO、MADDPG先跑统一reward再拆社交reward无梯度参数优化粒子群PSO初始粒子范围要覆盖解空间别贪快这32天我从“觉得算法很难”走到“觉得算法很杂”心态上反而放松了不少。我慢慢接受一个事实算法不是一个能“学完”的科目而是一个能“越用越熟”的工具箱。你不需要记住每个工具的说明书但你必须知道工具箱里有什么以及什么时候该翻出哪一件。搜索关键词里那些五花八门的算法名称其实对应着千千万万个正在解决具体问题的人——有人在做导航避障有人在做工业视觉有人在做多智能体训练有人只是想在下次面试里不再心虚。不管你是哪一种这条路的共同点都是动手写、动手调、动手记录。我现在就很庆幸自己从day01就开始写了这些“更弱智”的笔记至少它们让day32的我知道自己是从哪儿一步步走到这儿的。