当前位置: 首页 > news >正文

算法系列教程:1. BFS求无向无权图最短路径

示例图

3cd9e414-6dfa-4011-a030-5d6c5a685d39

BFS示例代码

function bfsShortestPath(graph, start, end) {const queue = [[start]];const visited = new Set([start]);while (queue.length > 0) {const path = queue.shift();const node = path[path.length - 1];if (node === end) return path;for (const neighbor of graph[node]) {if (!visited.has(neighbor)) {visited.add(neighbor);queue.push([...path, neighbor]);}}}return null;
}const graph = {A: ['C', 'B', 'D', 'E'],B: ['A', 'C', 'F', 'D'],C: ['A', 'B', 'F'],D: ['A', 'B'],E: ['A'],F: ['B', 'C']
};console.log(bfsShortestPath(graph, 'C', 'D'));

代码逐行解析

下面逐行解析这段代码在求解 C到D的最短路径 时的执行逻辑,重点跟踪队列、已访问集合的变化和路径探索过程:

1. 函数定义

function bfsShortestPath(graph, start, end) {
  • 定义函数bfsShortestPath,用于计算从start(此处为'C')到end(此处为'D')的最短路径。
  • 参数:graph(邻接表表示的无向无权图)、start(起始节点)、end(目标节点)。

2. 初始化队列和已访问集合

  const queue = [[start]];const visited = new Set([start]);
  • queue(队列):存储待探索的路径,初始时只有一条路径[start](即['C'],从C出发的初始路径)。
  • visited(集合):记录已访问的节点,避免重复探索(防止循环),初始时仅包含start(即'C')。

3. BFS核心循环(队列非空时持续探索)

  while (queue.length > 0) {
  • 只要队列中还有待探索的路径,就继续循环(BFS的核心:按“距离递增”顺序探索路径,确保第一个到达目标的路径是最短的)。

4. 取出队首路径并获取当前节点

    const path = queue.shift();const node = path[path.length - 1];
  • queue.shift():取出队列的第一个路径(队列“先进先出”特性,保证先探索距离C更近的路径)。
  • node:当前路径的最后一个节点(即“当前正在处理的节点”,后续需探索它的邻居)。

5. 判断是否到达目标节点

    if (node === end) return path;
  • 如果当前节点是目标节点end(即'D'),直接返回当前路径(BFS特性:此时的路径是最短路径)。

6. 遍历当前节点的所有邻居

    for (const neighbor of graph[node]) {
  • 遍历graph中当前节点node的所有邻居(即与node直接相连的节点)。

7. 处理未访问的邻居

      if (!visited.has(neighbor)) {visited.add(neighbor);queue.push([...path, neighbor]);}
  • 若邻居未被访问(!visited.has(neighbor)):

    • 标记为已访问(visited.add(neighbor)),避免重复处理。
    • 生成新路径(当前路径 + 该邻居),加入队列等待下一轮探索(queue.push([...path, neighbor]))。

8. 无路径时返回null

  }return null;
}
  • 若队列空了仍未找到D,说明C到D无路径,返回null(此处不触发,因为存在路径)。

9. 图的定义(邻接表)

const graph = {A: ['C', 'B', 'D', 'E'],B: ['A', 'C', 'F', 'D'],C: ['A', 'B', 'F'],D: ['A', 'B'],E: ['A'],F: ['B', 'C']
};
  • 定义无向无权图:每个节点的数组值是其直接相邻的节点。例如:

    • C的邻居是A、B、F(C与这三个节点直接相连)。
    • A的邻居包含D(A与D直接相连)。
    • B的邻居包含D(B与D直接相连)。

10. 调用函数并打印结果

console.log(bfsShortestPath(graph, 'C', 'D')); // 输出:['C', 'A', 'D']
  • 调用函数求C到D的最短路径,下面详细分析执行过程

关键执行步骤(C→D的探索过程)

初始状态:

  • 队列:[ ['C'] ](只有从C出发的初始路径)。
  • 已访问:{ 'C' }

第1次循环(处理路径['C']):

  • 取出路径['C'],当前节点node = 'C'

  • 判断:C !== D(继续)。

  • 遍历C的邻居:['A', 'B', 'F'](按数组顺序处理)。

    • 邻居A:未访问 → 标记visited = { 'C', 'A' },新路径['C', 'A']入队 → 队列:[ ['C', 'A'] ]
    • 邻居B:未访问 → 标记visited = { 'C', 'A', 'B' },新路径['C', 'B']入队 → 队列:[ ['C', 'A'], ['C', 'B'] ]
    • 邻居F:未访问 → 标记visited = { 'C', 'A', 'B', 'F' },新路径['C', 'F']入队 → 队列:[ ['C', 'A'], ['C', 'B'], ['C', 'F'] ]

第2次循环(处理路径['C', 'A']):

  • 取出路径['C', 'A'],当前节点node = 'A'

  • 判断:A !== D(继续)。

  • 遍历A的邻居:['C', 'B', 'D', 'E']

    • 邻居C:已访问(跳过)。
    • 邻居B:已访问(跳过)。
    • 邻居D:未访问 → 标记visited = { 'C', 'A', 'B', 'F', 'D' },新路径['C', 'A', 'D']入队 → 队列:[ ['C', 'B'], ['C', 'F'], ['C', 'A', 'D'] ]
    • 邻居E:未访问 → 标记visited = { 'C', 'A', 'B', 'F', 'D', 'E' },新路径['C', 'A', 'E']入队 → 队列:[ ['C', 'B'], ['C', 'F'], ['C', 'A', 'D'], ['C', 'A', 'E'] ]

第3次循环(处理路径['C', 'B']):

  • 取出路径['C', 'B'],当前节点node = 'B'

  • 判断:B !== D(继续)。

  • 遍历B的邻居:['A', 'C', 'F', 'D']

    • 邻居A、C、F:均已访问(跳过)。
    • 邻居D:已访问(第2次循环中已标记)→ 跳过(无新路径入队)。
  • 队列变为:[ ['C', 'F'], ['C', 'A', 'D'], ['C', 'A', 'E'] ]

第4次循环(处理路径['C', 'F']):

  • 取出路径['C', 'F'],当前节点node = 'F'
  • 判断:F !== D(继续)。
  • 遍历F的邻居:['B', 'C'] → 均已访问(无新路径入队)。
  • 队列变为:[ ['C', 'A', 'D'], ['C', 'A', 'E'] ]

第5次循环(处理路径['C', 'A', 'D']):

  • 取出路径['C', 'A', 'D'],当前节点node = 'D'
  • 判断:D === D → 满足条件,返回该路径。

最终结果

函数返回['C', 'A', 'D'],即C到D的最短路径是“C→A→D”(长度为2,经过2条边)。

补充说明

C到D其实还有另一条等长路径“C→B→D”(同样长度为2),但由于BFS中邻居的遍历顺序(C的邻居按['A', 'B', 'F']顺序处理),['C', 'A']路径先入队,因此其延伸出的['C', 'A', 'D']先被探索到,成为函数返回的结果。两条路径都是最短路径,BFS会返回“先被发现”的那条。

http://www.jsqmd.com/news/36745/

相关文章:

  • ESP-IDF引用自定义组件头文件失败
  • 2025年靠谱的装修品牌权威推荐
  • [Python刷题记录]-合并区间-普通数组/二维数组-中等
  • word导出图表 - IT
  • 2025年国内装修工程队排名:徐州领先企业一站式服务解析
  • 2025年评价高的学习能力少儿训练品牌选哪家
  • 2025年平床身数控车床生产厂家口碑排行榜
  • 2025年11月国内窗帘电机公司推荐排行榜
  • Blender科幻机甲娘莉莉魅魔人物角色3D模型带骨骼动作绑定带贴图
  • 2025年高科技数控机床供货商推荐
  • PR视频剪辑音频处理教程 School Of Motion – Premiere for Motion Designers
  • 行业内农业遮阳网渠道
  • 2025年智能中高考加盟电话推荐选哪家
  • AE插件-Furikake 1.0.0 Win 轻量级高性能粒子特效插件+使用教程
  • 有了 AI 编程工具 Cursor,前端开发 “消失”,又回归全栈开发模式
  • 邮件别名
  • 自定义redis列表增量迭代
  • 2025年IGBT锡膏供货商口碑排行榜
  • 升级不等待!Autodesk Inventor 2026:大装配优化 + 多格式兼容,机械工程师的效率利器
  • 2025年光伏锡渣还原粉定制厂家推荐
  • Raylib贴图
  • 重新开始记录Blogs,近年工作历程分享
  • 2025年雨棚企业推荐榜
  • 【Tools】Visual Studio利用经验介绍(包括基本功能、远程调试、引入第三方库等等)
  • 双鹿冰箱维修服务——服务随叫随到
  • 样本特征数据标准化
  • Claude Code用户故事编写最佳实践指导手册
  • 2025年毛发检测排名怎么选择
  • 2025年权威的形象思维少儿训练机构口碑推荐榜
  • 隐藏性很高的npm恶意依赖包