搜索算法可视化

搜索不仅要找到目标,还要决定先看哪里、哪些部分可以跳过。本组动画与代码同步,展示有序数组范围减半、图逐层扩展,以及启发式如何引导搜索边界。

线性查找不要求排序,从前到后检查,最坏情况读取全部元素。其他方法利用不同结构:二分用排序排除半个范围;双指针对有序数组求目标数对,从两端排除不可能的组合;本课滑动窗口要求连续子数组、非负值和正目标,左右边界都只向前走。前缀和押注数组不会变:先花一遍算出累计和,之后任意区间和都只需一次减法。BFS、DFS 则遍历图,以队列或栈决定下一点。A* 加入对剩余成本的估计,需要满足相应条件才能保留最短路保证。

如何阅读搜索算法

  • 排除了什么:二分每次探测排除约一半候选;图遍历则通过已访问标记避免重复处理。
  • 访问顺序是什么:BFS 用队列从近到远扩展,DFS 用栈或递归先深入再回退。邻接表下两者都是 O(V + E),轨迹却不同。
  • 需要什么前提:二分必须有序。BFS、DFS 可遍历一般图,而 BFS 的逐层顺序保证无权图中的最少边路径。
  • 何时可相信启发式:A* 按实际成本 g 加估计 h 排序。h 一致时(目标处为 0,每走一步下降不超过这一步的代价),本课这种不重开已关闭格子的实现也能保留最短路;高估时,第一个出队目标可能不是最优答案。

算法比较

算法对象时间额外空间前提或工具顺序
线性查找任意数组O(n)O(1)无排序要求从前到后
二分查找有序数组O(log n)O(1)有序范围减半
双指针有序数组O(n)O(1)有序两端靠拢
滑动窗口连续子数组O(n)O(1)本课非负值、正目标右侧扩展、左侧收缩
前缀和区间求和构建 O(n),每次查询 O(1)O(n)很少修改的数组两条分隔线,一次减法
广度优先搜索图/树O(V + E)O(V)队列从近到远
深度优先搜索图/树O(V + E)O(V)栈优先深入
树遍历二叉树O(n)O(h)递归或栈前序/中序/后序
连通分量无向图O(V + E)O(V)访问集合扫描并填充各个岛屿
A* 寻路带权网格/图本课 O((V + E) log V)O(V + E)满足最短路要求的启发式最小 g + h 优先

它们不全是在竞争同一个问题。二分问值是否在有序表中,图搜索问顶点如何连接。BFS 与 DFS 才是直接的顺序对照。A* 使用类似 Dijkstra 的优先队列,再加启发式,把搜索集中到目标方向。

建议学习顺序

先学线性查找。逐个检查,命中就停;最坏情况下要读完整个数组。它引出一个问题:多知道一些结构后,能跳过多少工作?

二分查找用一次比较排除半个候选范围。一百万个有序元素最多约二十次探测,对比线性查找最坏的一百万次。再学双指针,利用同样的排序,从两端移动索引。无序失败例会跳过真实数对,说明移动规则必须依赖排序。

滑动窗口让两个指针同向前进,右边加入、左边移除,每个元素至多各经历一次,因此总成本为 O(n)。本课的负数反例使单调性失效,收缩策略可能错过更短的合法窗口。

前缀和不再寻找窗口,而是预先算好所有答案:一遍 O(n) 写下累计和,任意区间和都是 prefix[r + 1] − prefix[l],不论长度都只读两个值。它的失败点是单点更新,会让之后所有累计和失效。

接着用 BFS从序列走向图,观察按层访问如何产生无权最短路,再用 DFS把队列换成栈,比较深入和扩散。树遍历把 DFS 用于二叉树,只改变在两个子调用前、之间或之后记录当前节点的时机,就得到前序、中序和后序。

连通分量在遍历外再加扫描:遇到未着色顶点就启动一次填充,启动次数等于岛屿数。孤立顶点本身也是一个分量。最后学 A*,把搜索边界从 FIFO/LIFO 改为按 g + h 排序,并通过高估启发式的反例检查最短路保证。

全部搜索可视化课程

改变输入或图,逐步执行,对照代码高亮检查访问与排除顺序。