算法工程师学习复盘:从排序、路径规划到3DCNN与检索引擎的完整笔记

算法工程师学习复盘:从排序、路径规划到3DCNN与检索引擎的完整笔记 今天是2026年3月22日我照例把这一天的算法学习内容整理成了笔记。我做算法方向开发也有几年了之前一直在做数据结构和基础算法后来又接触了图像处理、搜索排序、路径规划这些更偏应用的方向。平时工作里常常会碰到一类问题某个算法听起来很熟可一旦要真用起来就发现细节全是对不上的。所以我会不定期花一整天时间把脑子里边角角的东西拉出来重新梳理一遍然后落到笔记里。这篇笔记跟平时碎片化的记录不一样是一个比较完整的学习复盘。它覆盖了基础排序、图论最短路径、AGV路径规划、视频理解算法、图像ISP、检索引擎还有一批工程中高频出现的算法疑点。很多内容并不是今天才第一次学而是我在真实项目中踩过坑之后又回过头来向教科书验证了一遍。适合谁看我觉得如果你是刚准备校招或者转行做算法的开发这篇笔记能帮你在浩如烟海的算法清单里找到一条主线如果你已经在做工程开发里面关于踩坑的记录和性能选型的对比可能也会对你有用。整理的过程里我一边复习一边发现很多算法单看都是“会了”但连起来看就会冒出许多有意思的交叉点。比如排序不只是排序它是很多复杂算法的底层零件Dijkstra不只是图论题它和A*算法、和CBS多机器人路径规划在思想上是一脉相承的。这篇文章就直接按我当天的梳理顺序来写每一步都是实际做过、调过、验证过的结论。1. 为什么要把这么多算法放在同一天里过一遍3月22日这天我本来是没有开发任务的所以索性把之前积累的问题全部集中起来处理。处理的方式不是漫无目的地刷题而是按底层原理、工程应用、深度学习三个大块去复盘。底层原理管的是排序、搜索、图论这些基石工程应用则聚焦在路径规划、规则匹配、检索引擎这类特别依赖基本功的方向深度学习那部分主要是为了解决自己动手写3DCNN和用现成C3D预训练模型时的困惑。1.1 当天的学习主线基础、路径、深度模型我给自己定的主线很明确真正常用的算法花两个小时吃透而不是平均用力。所以我给排序列了比较高的优先级因为无论是写搜索系统还是做数据预处理排序都躲不开。图论相关的Dijkstra、A*算法只在真实地图或者网络拓扑中有实用价值所以我结合AGV多机器人路径规划来重看。深度学习和图像相关的算法比如3DCNN和C3D模型之间的关系、ISP里bayer2rgb的插值套路则通过案例来复盘。整个一天下来主线并没有走偏。1.2 这套笔记覆盖的范围和它的适用场景把热搜里那些词拼起来看2026年算法圈子关心的其实集中在几个点排序算法和数据结构依然是面试和基础工作的硬通货A*、粒子群、模拟退火这些常见于机器人和工业控制领域3DCNN、C3D、多模态融合则是深度学习应用的持续热点。所以我这份笔记有点像一个索引把这些方向都串了一遍。不管你是做Web后端、图像算法还是做AGV调度应该都能从中找到一段相关的内容如果你对这些方向都比较陌生也能通过这篇笔记建立一个零散但完整的算法版图。2. 基础算法里的那些精确细节远比背复杂度重要很多人学排序就记住一句话冒泡O(n^2)、快排O(n log n)、归并O(n log n)。这句话没有错但它离真正能写出可上线的代码差得非常远。下午我专门把冒泡排序、堆排序、归并排序的实现细节重新过了一遍还顺带把迪杰斯特拉负权值这个老坑拿出来复盘了一遍。这是因为之前有同事问我为什么带负权边的图用Dijkstra会算错我才意识到基础不牢的代价常常在代码review时才暴露。2.1 排序算法的工程取舍不止看时间复杂度的平均值以冒泡排序的C实现为例教科书版本通常是两层循环void bubbleSort(int arr[], int n) { for (int i 0; i n - 1; i) { for (int j 0; j n - i - 1; j) { if (arr[j] arr[j 1]) { std::swap(arr[j], arr[j 1]); } } } }这个实现是正确但如果数组已经基本有序它仍然会傻傻地跑满两层循环。所以工程里我们会加一个swapped标志位如果某一轮没有任何交换就直接跳出void bubbleSortOptimized(int arr[], int n) { for (int i 0; i n - 1; i) { bool swapped false; for (int j 0; j n - i - 1; j) { if (arr[j] arr[j 1]) { std::swap(arr[j], arr[j 1]); swapped true; } } if (!swapped) break; } }这个小优化后它在最好情况下的时间复杂度可以降到O(n)。真正工作了才发现这类针对特定输入的微调往往才是性能差异的来源。排序算法真正的难点不是代码而是什么时候用哪一个。堆排序的优势在于O(1)的额外空间归并排序的优势在于稳定且适合链表和外排序快排虽然平均快但最坏会退化到O(n^2)。我整理过一个选型建议小数据量比如几十个元素插入排序就够了需要稳定排序时优先归并内存紧张且不要求稳定时才考虑堆排序。2.2 Dijkstra为什么消化不了负权值迪杰斯特拉算法本身是一个非常经典的贪心算法。它维护一个尚未确定最短距离的集合每次都从里面取出距离起点最近的那个点并且认为一旦取出来它的最短距离就已经确定了。这在所有边权非负时确实是正确的因为其他路径想绕回这个点时只会把距离加长。可一旦出现负权边这个核心假设就会崩掉。比如有一条从A到B的边权为5但从A到C的边权为2、从C到B的边权为-4那么从A出发去B的最短路径其实是经过C而不是直接走A到B。但Dijkstra可能在处理到C之前就把B标记为已访问了等C更新B的时候已经追悔莫及。处理负权边的常规做法是Bellman-Ford算法它通过多轮松弛保证每条边都被反复地用来刷新距离。SPFA是Bellman-Ford的一个队列优化版平均表现通常不错但最坏复杂度依然可能很高。工程上如果确认没有负权就放心用Dijkstra加优先队列如果存在负权我一般倾向于直接上Bellman-Ford因为稳定而且实现逻辑简单不容易写出隐藏问题。2.3 数据结构和算法是面试的度量尺却不是全部热词里有一条是“java外包也考算法吗”这个问题我看到以后很有感触。答案是需要考但难度通常没有大厂那么离谱更看重的是能不能用代码解决实际问题。面试里高频出现的排序算法与其说是考你背不背得出来不如说是考你能否用严谨的逻辑把边界情况处理好。字节跳动的算法题也是这样本质上是想通过一个不长的题目快速了解你的抽象能力、代码风格和沟通思路。套路是没有用的真正有效的是把那些常见数据结构夯实数组、链表、栈、队列、哈希表、二叉树、堆、并查集然后在上面做有限制的练习。3. 路径规划系列从A*到多机器人CBS的一脉相承下午的重头戏给了路径规划。热词里同时出现了“A算法”、“Dijkstra算法”和“一种基于改进冲突搜索的多机器人路径规划算法”说明AGV领域这部分技术确实很受关注。我在实际做AGV调度平台的时候发现路径规划不是单机跑一遍A就结束的而是先解决单机“如何走得最优”再解决多机“如何不打架”。3.1 A*算法在AGV落地时要注意的真实细节A算法的核心公式是f(n) g(n) h(n)其中g(n)是从起点到当前节点的实际代价h(n)是当前节点到终点的启发式估计代价。只要h(n)满足可采纳性那么A就能找到最优解。在AGV场景里地图通常被栅格化所以曼哈顿距离是一个很自然的选择因为AGV的运动方向一般限制为上下左右四个方向。不过我在实际项目里踩过几个坑。第一个是频繁使用std::priority_queue后出现了重复节点问题同一个节点可能多次进入开放列表。我的做法是用一个std::unordered_map记录每个节点当前最优的g(n)如果新扩展的g(n)比已有的还差就跳过避免无效计算。第二个是启发函数权重设置理论上不加权的A*一定能找到最短路径但在地图很大的时候搜索节点数量会爆炸所以很多工程实现会引入f(n) g(n) w * h(n)其中w大于1搜索速度快很多但不再保证最短路径。AGV任务往往更看重耗时稳定能接受次优路径所以w取1.0到1.5之间的调参是常有的事。struct Node { int x, y; float g, h, f; bool operator(const Node o) const { return f o.f; } }; float heuristic(int x1, int y1, int x2, int y2) { return std::abs(x1 - x2) std::abs(y1 - y2); }第三个坑是A跑出来的路径往往紧贴障碍物边缘真实AGV有物理半径贴墙走容易剐蹭。后续我一般是把地图先做一次膨胀把障碍物按车辆半径向外扩大之后再用A规划这样得到的路径安全性会好很多。这个步骤听着基础却常常在demo阶段被忽略。3.2 多机器人路径规划与冲突搜索的升级思路单机路径规划相对成熟多机场景就复杂得多。如果有两辆AGV同时规划各自的A*路径可能共用同一段路于是产生冲突。处理多机冲突的一种主流思想是CBS也就是基于冲突的搜索它把问题分成两层上层处理整体路径组合下层为每台车跑单机路径。当发现两个机器的路径在某个时间点占用同一个位置时就在上层给其中一台机器加一个约束让它在这个时间点不能出现在那个位置然后重新为这台机器规划路径。我在看到“基于改进冲突搜索的多机器人路径规划算法”这种论文标题时特别有共鸣。改进方向通常集中在几个维度一是如何快速识别关键冲突而不是把精力耗在和最终结果无关的冲突上二是如何复用已经规划好的路径比如只重新规划被约束的车辆而不必让所有车辆全部从头开始三是引入优先级或者成本函数让调度更贴合AGV的实际任务。这一块和纯A*的最大区别在于你需要从系统角度思考每条路径之间的相互影响不能只盯着单个目标的最优。3.3 粒子群、模拟退火这些智能算法到底用在哪一层热词里的“粒子群算法原理”和“模拟退火算法”让我想起很多同学的一个误区以为智能优化算法可以替代A去直接做路径规划。实际上A这种算法适合解决确定性的离散搜索问题而粒子群、模拟退火更擅长在连续、高维、目标函数不太好求导的场景里找到一个可接受的解。比如AGV调度里任务排序和充电时机组合优化就可以用粒子群如果解空间超大且对全局最优要求不高模拟退火也能快速给出工程可用结果。粒子群的核心是每个粒子代表一个候选解通过向个体历史最优和群体历史最优学习来更新速度和位置。它自己并不保证能找到全局最优但胜在实现简单、并行化友好。做工程时我给的判断标准很简单如果问题能用确定性规则枚举清楚优先用确定性算法只有问题实在太大或者非凸严重才考虑粒子群、模拟退火这类启发式算法。不要因为名字听起来聪明就用它而要看它能不能在可接受时间内给一个可用的解。4. 视频理解方向的疑问3DCNN和C3D到底是不是一类算法短视频、安防监控、工业质检里都经常用到视频理解热词中直接出现“3dcnn和c3d算法是一种算法吗”。我在学习这块时同样有过困惑查了不少资料又把代码跑了一遍才理清楚。4.1 3DCNN和C3D的关系一句话拆清楚2DCNN处理的是单张静态图片卷积核在空间上做滑窗3DCNN处理的是连续多帧组成的视频块卷积核不仅在宽度和高度方向上滑动还在时间维度上滑动因此能同时提取空间特征和短期时间运动特征。C3D并不是一个和3DCNN完全无关的单独算法它是3DCNN的一种经典网络结构由Tran等人在2015年提出全称是Convolutional 3D核心是一组结构规整的3D卷积层加3D池化层比较著名的结论是3x3x3的卷积核在视频任务上表现特别稳。所以如果问题是“3DCNN和C3D是不是同一种算法”我会说C3D是3DCNN模型里的一个代表两者不是竞争关系而是包含关系。实际使用中直接用C3D做视频分类输入通常是16帧连续图像每帧裁剪成112x112分辨率。网络包含8次卷积、5次池化最后接两个全连接层和一个softmax分类层。跑一次前向计算量不小所以真正落地时往往会用I3D、SlowFast或者带3D卷积的Transformer结构。但有一点没有变先想清楚任务到底需要多长的时序上下文如果只是单帧判断就能做完全没必要引入3D卷积。4.2 图像算法从bayer2rgb开始ISP里的插值挑战视频理解建立在清晰图像之上而图像又常从CMOS传感器输出的bayer格式开始。这个热词的出现说明不少做图像算法的人都在关心底层ISP流程。bayer格式每个像素只保存R、G、B三种颜色中的一种通常按照RGGB或BGGR的排列方式交错bayer2rgb就是通过插值把每一个像素缺失的另外两个颜色通道估算出来。最简单的双线性插值速度快但容易产生伪彩色尤其是边缘和纹理区域。工程上为了兼顾效果和算力会采用边缘感知插值或者色差域插值先判断当前像素所处的是水平边缘还是垂直边缘再决定用哪个方向的邻域。这里容易被忽略的是通道比例问题。人眼对绿色更敏感所以bayer排列里绿色像素数量占一半红色和蓝色各四分之一。颜色重建完后还要做白平衡、gamma校正、色彩矫正矩阵、降噪等处理这部分通常叫ISP pipeline。我在做摄像头图像优化时经验是一次只动一个环节否则很难判断图像变差来自哪个模块。而且bayer2rgb这一步对后续所有模块的影响都很大插值质量不行后面的降噪和锐化怎么调都觉得不对劲。4.3 多模态融合和分类模型的复习要点热词里还出现了“多模态融合算法”和“图像分类算法”这两个概念最近在电商、安防、医疗影像里都特别常见。多模态融合不是在模型最后把特征向量拼在一起那么简单更常见的做法是分前端编码、中间融合、输出决策三个层次文本、图像、音频先各自经过编码器变成向量再通过注意力机制计算模态间的关系最后融合打分。做多模态项目时真正决定上限的往往是数据对齐也就是不同模态的数据在时间或语义上对得齐不齐比如视频语音和字幕如果错位超过几百毫秒再强的融合结构也救不回来。图像分类算法则偏向成熟工程。EVA-02这种大规模视觉Transformer模型在分类任务上表现强劲但部署成本很高。通常我会建议团队先在轻量级CNNs上跑通数据链路比如ResNet或者MobileNet确认业务问题和数据标注没有问题再上大模型训一版对比效果。“先简单模型验证、再复杂模型提分”是项目推进的低成本策略。5. 搜索、规则引擎和经典机器学习里的算法底子算法不止存在于图像和路径里搜索排序、实时规则匹配也是个人开发者或者后端工程师每天都会打交道的场景。热词里出现了BM25、Rete规则引擎、还有pid和foc这类工业算法我觉得可以放在一起复盘因为它们都在用非常朴素的原理解决非常实际的问题只是包装不同而已。5.1 Rete规则引擎到底在加速什么Rete算法是规则引擎的核心专门用来加速大量规则条件和事实对象之间的匹配。直观的理解是如果没有Rete每来一个新的事实就要把所有规则从头到尾重新匹配一遍而Rete把规则拆解成共享的匹配网络不同规则之间相同的条件会被复用同时它会保存部分匹配的中间结果因此当事实增、删、改时只需要增量更新受影响的分支不需要全量重算。刚开始看Rete时容易懵因为里面一堆Alpha节点、Beta节点和Join节点。举个生活化的例子它就像一个大型公司的简历筛选系统假如一堆岗位都要求“本科学历”这个条件只检查一遍而不是每个岗位都检查一遍。每次有新人进入人才库面试系统先做通用条件过滤再走各岗位专属的后续判断已经符合一半条件的人会挂起等待后续条件补齐。Rete模式匹配的核心思想就是“共享子条件 保存中间状态”理解了这一句实现代码时就有方向感。5.2 BM25排序公式的工程解读BM25是一种经典的信息检索排序算法现代搜索引擎很多基于词频统计的底层排序都受它的影响。它的公式没有神经网络那么玄乎本质上是综合考虑词频和逆文档频率后给查询和文档打一个相关分BM25(doc) Σ IDF(q_i) * (tf(q_i, doc) * (k1 1)) / (tf(q_i, doc) k1 * (1 - b b * len(doc) / avgdl))这里tf是词频k1和b是两个超参数len(doc)是文档长度avgdl是文档平均长度。第一眼看上去觉得复杂其实它要解决的痛点很具体如果一个词在文档里出现次数太多对相关度的贡献不能无限线性增长所以用tf / (tf 常数)类结构做饱和控制同时文档越长就越有可能因为篇幅大包含更多词所以引入文档长度归一化来防止长文档占便宜。在真实搜索系统里BM25并不是单独存在而是和query分析、倒排索引配合起来使用。我会先做分词和停用词过滤然后构造倒排索引查询时拉取候选文档再用BM25计算精确分数做粗排。它比向量检索更具解释性而且不需要复杂的训练流程所以我始终觉得任何做搜索算法的人都应该先把BM25从公式到代码完整实现一遍。5.3 PID、FOC、PTP这些工业控制算法的存在感热词里有PID算法和FOC算法还有PTP时延补偿可能来自做电机控制或者工业通信的朋友。PID是工业界最常见的闭环控制算法比例项对应当前误差的即时纠正积分项处理过去累积的小偏差微分项提前抑制误差变化趋势。实际调参的时候我常给新人说一句先把I和D全部设为0只调P让系统稳定再一点点加I消除稳态误差最后根据震荡情况加D。这个顺序能省很多时间。FOC是永磁同步电机控制中常用的磁场定向控制算法核心是把三相电流通过坐标变换变成旋转坐标系里的直轴和交轴电流然后分别控制磁通和转矩这样就能把交流电机控制得像直流电机一样直觉。PTP非对称时延补偿则是工业网络同步里一个非常细的点普通时间同步假设收发时延是对称的但PTP over E1这类链路里上下行时延明显不一样所以需要软件伺服补偿来纠正offset。看这几个词的时候我非常感慨很多时候大家以为算法就是机器学习其实真正离设备最近的算法往往是这种精准的数学控制逻辑。5.4 国密SM2、SM3、SM4和AES128CMAC那一类算法问题热词里提到了“国密sm2、sm3、sm4算法(js、java版)”以及在线的“aes128cmac算法”。这些都是密码学算法跟前面那些数据算法完全不同。SM2是椭圆曲线公钥密码算法用于签名和密钥交换SM3是密码杂凑算法输出256位摘要类比SHA-256SM4是分组密码算法分组长度128比特密钥长度也是128比特。做国产化或者等保合规项目时需要把SM2/SM3/SM4在前后端同时实现常会碰到JS和Java加密结果对不上的问题。解决的办法通常不是网上到处复制代码而是先确定好填充模式、编码方式和密钥格式尤其要注意十六进制字符串和Base64两种转换方式是否统一。AES128CMAC则更像是消息认证场景AES加密后通过CBC-MAC方式生成一个固定长度的认证码用来确认消息没有被篡改。这类密码算法最大的坑不是算法本身而是模式参数。在线计算工具可以当验证手段但真正的生产代码一定要把底层库和标准文档对齐。我在做接口签名算法时会先用一组官方Test Vector验证两个不同语言的实现是否保持一致只有向量完全一致才敢接入业务。6. 从准备面试到写工程的算法题心法一天学下来我也专门空出一段时间回顾算法题库和面试心得。毕竟算法学习不仅要会用很多时候还要在考场上写出来。热词里有“字节跳动 算法题”和“java外包也考算法吗”说明大家对这个话题依然很关注。我的观点是算法题备考最好跟工程实践并行而不是割裂。6.1 算法题到底在考哪些能力字节跳动这类公司的算法题核心不是考你有没有背过某道题而是看你遇到一个从没见过的问题时是否有一套稳定的思考框架。这个框架通常是先澄清题目中的输入规模和数据范围思考暴力解法能不能过然后分析是否有重复计算可以优化再考虑正确的数据结构是什么。比如一个“求数组第K大”的题目最简单是排序后取下标时间O(n log n)进阶用快排partition思想可以做到平均O(n)如果数据是动态插入的需要维护一个大小为K的小顶堆。能把三种解法都完整分析出来比直接默写堆排序更让面试官认可。6.2 外包岗位的算法考察特点外包面试也考算法但更偏向基础语法和常用的数据结构的应用。比如链表反转、括号匹配、字符串去重这类问题出现的频率就很高。准备时要特别注意手写代码的完整度因为外包岗对coding能力非常看重可能会要求你在共享文档里现场写一个完整函数并正确处理边界条件。我的建议是不要只刷困难题简单和中等的题要多写几遍做到不用IDE也能写出无语法错误的代码。这个能力在真正工作中也有用开会时随手在文档里写一段逻辑会非常加分。6.3 一个典型的归并排序变形题记录复习时我做了一个典型的归并排序变体计算数组中的逆序对。核心思路是在归并两个有序子数组时如果右边数组的一个元素小于左边数组某个元素那么左边数组从这个元素到末尾的所有元素都和它构成逆序对。直接两层循环的复杂度是O(n^2)归并写法可以降到O(n log n)。这道题和纯归并排序最大的区别在于merge阶段要额外累加一个计数器其他地方可以复用归并模板。把它写完以后我有种很强烈的感觉把一道经典题吃透确实能迁移到许多看似新奇的题目中。7. 半天整理出的常见问题速查表为了后续查找方便我把今天在所有热词涉及的问题里挑出最容易混淆的几个做成了速查表。这个表对我的实际帮助很大很多概念记在脑子里会随时间模糊查表则能快速唤醒。问题一句话结论3DCNN和C3D是一种算法吗C3D是3DCNN的一种经典网络结构3DCNN是更大的技术类别Dijkstra能处理负权值吗不能负权边会导致贪心失效应改用Bellman-Ford或SPFAA*一定能找到最短路径吗启发函数满足可采纳时可以实际工程常加大权重牺牲最优换速度BM25的主要作用根据词频和文档长度等信息计算文本相关度用于搜索粗排Rete算法解决什么问题避免规则引擎每次全量匹配提升规则匹配性能粒子群和模拟退火适合什么连续、高维、非凸的优化问题不保证全局最优但能快速给出可行解bayer2rgb难在哪里插值时容易产生伪彩色边缘方向判断是关键AES128CMAC和SM4的区别前者是做完整性认证的MAC算法后者是对称加密算法FOC和PID有什么关系FOC控制电流环、速度环内部常用PID作为闭环控制器PTP非对称时延补偿干嘛用修正双向链路时延不对称带来的时间同步误差如果要把这天的笔记压缩成一句话我的体会是算法学习并不是以“见过更多算法”为目标而是要以“理解每个算法解决的核心痛点和代价”为目标。排序的代价是数据规模下的时间空间取舍A*的代价是搜索效率与最优性的平衡BM25的代价是词频饱和与文档长度归一化。读懂这些代价之后遇到新场景才知道如何选型而不是照搬一套代码上去碰运气。8. 实践之后的一个小建议当天学完必须用代码重构一遍我每回顾一种算法尤其是A*、BM25、Rete这类能够在工程里落地的内容都会坚持做一件事把论文或博客里的伪代码用自己熟悉的语言重新实现一遍并跑真实的测试用例验证。如果只是看懂了示意图过了三五天基本就忘了细节只有亲手碰到过“队列为空时如何处理”“启发函数越界导致索引异常”这些边界情况知识才真正长在你身上。这次3月22日的笔记里最后的复盘时间留给了代码验证。我把A*扩展到了带时间维度的路径搜索模拟两台机器在交叉路口的避让给BM25补了文档长度归一化的实现还用归并排序的模板把逆序对计数重写了一遍。这些代码不算有多高大上但每个都验证了我当天做的笔记判断是对的。算法这条路没有捷径但如果你也按“白天理解原理、晚上亲自编码验证”的节奏来积累的速度会比自己盲目刷题快得多。