数学建模优秀论文算法-广度优先搜索(BFS)

广度优先搜索(BFS)入门教程:从迷宫到图的"逐层探索"

1. 开篇导言:用一个迷宫问题认识BFS

假设你站在迷宫的入口,面前有若干条岔路。你想最快找到出口------也就是走最少的步数到达终点。这时,你会怎么选?如果你像"洪水漫延"一样:先探索入口周围1步内的所有岔路,再探索2步内的,依此类推......直到找到出口。这种"从近到远、逐层扩散"的策略,就是**广度优先搜索(Breadth-First Search, BFS)**的核心思想。

BFS是图论中最基础、最常用的遍历算法之一,能解决无权图最短路径、连通性判断、层次遍历等经典问题。本文会用最通俗的语言,带你从0到1掌握BFS。

2. 背景溯源:BFS从哪里来?

BFS的诞生与图论的发展密切相关:

  • 19世纪,欧拉解决"柯尼斯堡七桥问题",开创了图论;
  • 20世纪50年代,人们开始研究"如何高效遍历图中的所有节点"------BFS正是其中的经典解法;
  • 1959年,美国数学家Edward F. Moore和工程师C. Y. Lee分别独立提出BFS:Moore用它解决"迷宫最短路径",Lee用它解决"电路布线"问题。

如今,BFS已渗透到计算机科学的各个领域:从搜索引擎的网页爬行,到游戏中的AI路径规划,再到社交网络的好友推荐,都能看到它的影子。

3. 前置知识:先搞懂3个基础概念

在学习BFS前,你需要先理解3个"底层积木"------图的基本概念队列结构访问标记

3.1 图:BFS的"战场"

图是BFS的操作对象,它由两个核心部分组成:

  • 节点(Vertex) :代表问题中的"实体"(比如迷宫的岔路口、网页、社交网络的用户),用vvv表示;
  • 边(Edge) :代表节点间的"关系"(比如迷宫的通道、网页的超链接、好友关系),用(u,v)(u,v)(u,v)表示。

根据边的属性,图可以分为:

  • 无权图/有权图:边是否有"长度"(比如迷宫的步数是无权,公路的里程是有权);
  • 无向图/有向图:边是否有"方向"(比如朋友关系是无向,网页链接是有向)。

BFS的核心应用场景是"无权图"------因为它能找到"最短步数"的路径。

3.2 队列:BFS的"调度员"

队列是一种**先进先出(FIFO)**的线性结构,像银行排队:先到的人先办理业务,后到的人排队等待。

BFS中,队列的作用是维护"待探索的节点":我们先处理当前层的节点,再将它们的邻居加入队列,确保"逐层探索"的顺序。

队列的基本操作:

  • 入队(enqueue(Q,v)enqueue(Q, v)enqueue(Q,v)):把节点vvv加入队列尾部;
  • 出队(dequeue(Q)dequeue(Q)dequeue(Q)):从队列头部取出一个节点。

3.3 访问标记:避免"绕圈"的关键

想象你在迷宫里走,如果不做标记,你可能会反复走同一条路------BFS中也是如此。访问标记 是一个布尔数组visitedvvisitedvvisitedv,用来记录节点vvv是否已经被探索过:

  • visitedv=Truevisitedv = Truevisitedv=True:节点vvv已被处理,无需再访问;
  • visitedv=Falsevisitedv = Falsevisitedv=False:节点vvv未被处理,等待探索。

如果没有访问标记,无向图中的边(u,v)(u,v)(u,v)会导致无限循环(uuu处理vvv,vvv又处理uuu,反复入队)。

4. BFS的核心思想:逐层扩散,先到先得

BFS的本质是**"以起点为中心,像洪水一样逐层淹没周围节点"**------每一步都探索当前节点的所有"未被淹没"的邻居,直到所有可达节点都被覆盖。

用更通俗的话讲:

  • 你站在起点sss,先喊一声:"我在这里!";
  • 听到声音的邻居(1步内的节点)回应你,加入"待处理队列";
  • 你依次处理这些邻居,让它们再喊一声,吸引它们的邻居(2步内的节点);
  • 重复这个过程,直到没有新的节点回应。

这种策略的关键优势第一次到达某个节点时,走过的步数一定是最短的(因为按"距离从小到大"探索)。

5. BFS的算法原理:拆解每一步逻辑

现在,我们把"洪水漫延"的过程转化为可执行的算法步骤。首先,需要明确两个核心要素:

5.1 图的表示:如何存储节点与边?

为了让计算机理解图,我们需要用数据结构表示节点间的连接关系。最常用的两种方式是邻接表邻接矩阵

(1)邻接表(Adjacency List)

用一个数组(或字典)存储每个节点的"邻居列表"。例如,节点uuu的邻居列表记为Adj(u)Adj(u)Adj(u)。

  • 示例:图s→a,s→b,a→c,b→ds→a, s→b, a→c, b→ds→a,s→b,a→c,b→d的邻接表为:Adj(s)=a,bAdj(s) = a, bAdj(s)=a,b,Adj(a)=s,cAdj(a) = s, cAdj(a)=s,c,Adj(b)=s,dAdj(b) = s, dAdj(b)=s,d,Adj(c)=aAdj(c) = aAdj(c)=a,Adj(d)=bAdj(d) = bAdj(d)=b

邻接表的优势是节省空间(只存储存在的边),适合大多数图。

(2)邻接矩阵(Adjacency Matrix)

用一个V×VV×VV×V的二维数组MMM(VVV是节点总数),其中Muv=1Muv = 1Muv=1表示节点uuu和vvv之间有边,Muv=0Muv = 0Muv=0表示无边。

  • 示例:上述图的邻接矩阵(节点顺序s,a,b,c,ds,a,b,c,ds,a,b,c,d)为:M=0110010010100010100000100 M = \begin{bmatrix} 0 & 1 & 1 & 0 & 0 \\ 1 & 0 & 0 & 1 & 0 \\ 1 & 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ \end{bmatrix} M= 0110010010100010100000100

邻接矩阵的优势是查询边是否存在更快 ,但空间复杂度高(O(V2)O(V^2)O(V2)),适合节点少的图。

5.2 BFS的核心逻辑:"队列+访问标记"的循环

BFS的目标是从起点sss出发,遍历所有可达节点,并记录每个节点到sss的最短距离

我们用三个变量实现这个逻辑:

  • 队列QQQ:待处理的节点;
  • 访问标记数组visitedvisitedvisited:记录节点是否已处理;
  • 距离数组distdistdist:记录每个节点到sss的最短步数(dists=0dists = 0dists=0,未可达节点记为−1-1−1)。

6. BFS完整求解步骤:从初始化到结果输出

我们以**"无权图最短路径问题"**为例,详细拆解BFS的每一步(假设图用邻接表表示)。

6.1 问题定义

给定无权无向图G=(V,E)G=(V,E)G=(V,E)(VVV是节点集合,EEE是边集合),起点s∈Vs∈Vs∈V,求:

  1. 所有从sss可达的节点;
  2. 每个可达节点到sss的最短距离(即最少步数)。

6.2 步骤1:初始化变量

  • 队列初始化 :将起点sss加入队列(enqueue(Q,s)enqueue(Q, s)enqueue(Q,s));
  • 访问标记初始化 :标记sss为已访问(visiteds=Truevisiteds = Truevisiteds=True);
  • 距离数组初始化 :dists=0dists = 0dists=0(起点到自身的距离为0),其余节点distv=−1distv = -1distv=−1(未可达)。

6.3 步骤2:循环处理队列(核心!)

当队列QQQ不为空时,重复以下操作:

  1. 取出队首节点 :从队列头部取出一个节点uuu(u=dequeue(Q)u = dequeue(Q)u=dequeue(Q));
  2. 遍历邻接节点 :遍历uuu的所有邻居v∈Adj(u)v∈Adj(u)v∈Adj(u);
  3. 检查未访问节点 :如果visitedv=Falsevisitedv = Falsevisitedv=False(vvv未被处理):
    • 标记vvv为已访问(visitedv=Truevisitedv = Truevisitedv=True);
    • 更新vvv的距离(distv=distu+1distv = distu + 1distv=distu+1,因为uuu到sss需要distudistudistu步,vvv是uuu的邻居,所以多1步);
    • 将vvv加入队列尾部(enqueue(Q,v)enqueue(Q, v)enqueue(Q,v))。

6.4 步骤3:输出结果

循环结束后:

  • visitedv=Truevisitedv = Truevisitedv=True的节点是从sss可达的节点;
  • distvdistvdistv的值是该节点到sss的最短距离。

6.5 示例演示:用简单图验证步骤

我们用一个5节点图 (s,a,b,c,ds,a,b,c,ds,a,b,c,d)演示BFS过程,图结构如下:

  • sss连接aaa和bbb;
  • aaa连接ccc;
  • bbb连接ddd;
  • ccc连接ddd。

邻接表:Adj(s)=a,bAdj(s)=a,bAdj(s)=a,b,Adj(a)=s,cAdj(a)=s,cAdj(a)=s,c,Adj(b)=s,dAdj(b)=s,dAdj(b)=s,d,Adj(c)=a,dAdj(c)=a,dAdj(c)=a,d,Adj(d)=b,cAdj(d)=b,cAdj(d)=b,c

初始化后状态
  • Q=sQ = sQ=s
  • visited=True,False,False,False,Falsevisited = True, False, False, False, Falsevisited=True,False,False,False,False(假设节点顺序s,a,b,c,ds,a,b,c,ds,a,b,c,d);
  • dist=0,−1,−1,−1,−1dist = 0, -1, -1, -1, -1dist=0,−1,−1,−1,−1
第一次循环(处理sss)
  1. 取出队首节点sss;
  2. 遍历sss的邻居aaa和bbb:
    • 对于aaa:visiteda=Falsevisiteda = Falsevisiteda=False → 标记visiteda=Truevisiteda = Truevisiteda=True,dista=0+1=1dista = 0+1=1dista=0+1=1,入队Q=a,bQ=a,bQ=a,b
    • 对于bbb:visitedb=Falsevisitedb = Falsevisitedb=False → 标记visitedb=Truevisitedb = Truevisitedb=True,distb=0+1=1distb = 0+1=1distb=0+1=1,入队Q=a,bQ=a,bQ=a,b
第二次循环(处理aaa)
  1. 取出队首节点aaa;
  2. 遍历aaa的邻居sss和ccc:
    • 对于sss:visiteds=Truevisiteds = Truevisiteds=True → 跳过;
    • 对于ccc:visitedc=Falsevisitedc = Falsevisitedc=False → 标记visitedc=Truevisitedc = Truevisitedc=True,distc=1+1=2distc = 1+1=2distc=1+1=2,入队Q=b,cQ=b,cQ=b,c
第三次循环(处理bbb)
  1. 取出队首节点bbb;
  2. 遍历bbb的邻居sss和ddd:
    • 对于sss:visiteds=Truevisiteds = Truevisiteds=True → 跳过;
    • 对于ddd:visitedd=Falsevisitedd = Falsevisitedd=False → 标记visitedd=Truevisitedd = Truevisitedd=True,distd=1+1=2distd = 1+1=2distd=1+1=2,入队Q=c,dQ=c,dQ=c,d
第四次循环(处理ccc)
  1. 取出队首节点ccc;
  2. 遍历ccc的邻居aaa和ddd:
    • 对于aaa:visiteda=Truevisiteda = Truevisiteda=True → 跳过;
    • 对于ddd:visitedd=Truevisitedd = Truevisitedd=True → 跳过。
第五次循环(处理ddd)
  1. 取出队首节点ddd;
  2. 遍历ddd的邻居bbb和ccc:
    • 对于bbb:visitedb=Truevisitedb = Truevisitedb=True → 跳过;
    • 对于ccc:visitedc=Truevisitedc = Truevisitedc=True → 跳过。
最终结果
  • 可达节点:s,a,b,c,ds,a,b,c,ds,a,b,c,d(全部可达);
  • 距离数组:dist=0,1,1,2,2dist = 0,1,1,2,2dist=0,1,1,2,2 → 符合预期(sss到ccc需2步,sss到ddd需2步)。

7. BFS的算法分析:时间与空间复杂度

7.1 时间复杂度

BFS的时间复杂度是**O(V+E)O(V+E)O(V+E)**(VVV是节点数,EEE是边数):

  • 每个节点入队一次、出队一次 :时间O(V)O(V)O(V);
  • 每条边被遍历一次 (无向图的边会被两个节点各遍历一次,但总次数是O(E)O(E)O(E))。

这是图遍历的最优时间复杂度(必须访问所有节点和边)。

7.2 空间复杂度

BFS的空间复杂度是**O(V)O(V)O(V)**:

  • 队列QQQ的最坏情况:存储所有节点(比如完全二叉树的最后一层,占V/2V/2V/2节点);
  • 访问标记数组visitedvisitedvisited和距离数组distdistdist:各占O(V)O(V)O(V)空间。

8. BFS的适用边界:什么时候该用BFS?

BFS的核心优势是"按距离从小到大探索",因此它适合以下场景:

8.1 适用场景1:无权图的最短路径

BFS是无权图最短路径的唯一最优算法------因为第一次到达某个节点时,走过的步数一定是最少的(比如迷宫问题、社交网络的"一度好友""二度好友")。

8.2 适用场景2:图的连通性判断

判断两个节点是否连通(比如"你和小明是否有共同好友"),或统计图中的连通分量(比如"朋友圈有多少个独立的小团体")。

8.3 适用场景3:树的层次遍历

树是特殊的图(无环、连通),BFS的层次遍历是树的经典应用(比如"按层打印二叉树":根节点→第一层→第二层→...)。

8.4 不适用场景:这些问题别用BFS!

  1. 有权图的最短路径 :比如公路的里程(边权不同),需用Dijkstra算法 (边权非负)或Bellman-Ford算法(边权有负);
  2. 求所有路径 :比如"从sss到ttt的所有路径",BFS无法记录所有路径,需用深度优先搜索(DFS)
  3. 深度优先的问题:比如"八皇后""数独求解",需要回溯的问题,BFS效率极低。

9. 常见误区与注意事项

9.1 误区1:忘记标记访问→无限循环

如果不标记visitedvvisitedvvisitedv,无向图中的边(u,v)(u,v)(u,v)会导致uuu和vvv反复入队,无限循环(比如uuu处理vvv,vvv又处理uuu)。

9.2 误区2:入队后再标记访问→重复入队

正确的顺序是**"标记访问后再入队"**,而不是"入队后再标记"。如果先入队再标记,同一节点可能被多次入队(比如uuu和vvv都连接www,uuu先处理www并入队,vvv处理www时www还没被标记,会再次入队)。

9.3 误区3:混淆队列与栈→变成DFS

如果把队列换成 (后进先出),BFS就变成了深度优先搜索(DFS)------探索顺序从"逐层"变成"深入到底",结果完全不同。

9.4 误区4:BFS能解决所有最短路径问题

只有无权图边权相同的图(比如所有边权都是1),BFS的"最短步数"才等于"最短距离"。如果边权不同(比如有的边权是2,有的是1),BFS会失效。

10. 总结:BFS的"灵魂"是什么?

BFS的核心是**"逐层扩散,先到先得"**------它像一把"扇子",从起点慢慢展开,覆盖所有可达节点。

记住这三句话,你就掌握了BFS的本质:

  1. 用队列维护"待探索的节点",保证顺序;
  2. 用访问标记避免重复,防止循环;
  3. 第一次到达节点时,距离一定最短(无权图)。

11. 练习建议:从简单例子开始

  1. 二叉树的层次遍历:用BFS按层打印二叉树(比如根节点→左孩子→右孩子→左孩子的左孩子→...);
  2. 迷宫问题:用二维数组表示迷宫(0是通路,1是墙壁),求从入口到出口的最短路径;
  3. 朋友圈问题:用邻接表表示朋友关系,求有多少个独立的朋友圈(连通分量)。

最后的话

BFS是图论的"入门钥匙",它的逻辑简单但应用广泛。刚开始学习时,建议用纸笔模拟小例子(比如上述5节点图),一步步走流程------当你能手动写出BFS的每一步,就真正理解了它的核心!

下次遇到迷宫问题,不妨想想:"如果我是洪水,会怎么漫延?"------这就是BFS的思维方式。

案例介绍

给定一个5节点的无权无向图,节点分别为's','a','b','c','d',边的连接关系为:s连接a和b,a连接s和c,b连接s和d,c连接a和d,d连接b和c。使用BFS算法求解从起点's'出发到所有可达节点的最短距离。


Python代码

python 复制代码
from collections import deque

def build_graph():
    # 构建邻接表表示图结构,键为节点,值为邻居节点列表
    graph = {
        's': ['a', 'b'],
        'a': ['s', 'c'],
        'b': ['s', 'd'],
        'c': ['a', 'd'],
        'd': ['b', 'c']
    }
    return graph

def bfs_shortest_path(start_node, graph):
    # 初始化距离字典,所有节点距离设为-1(不可达)
    distance = {node: -1 for node in graph}
    # 初始化访问标记字典,所有节点设为未访问
    visited = {node: False for node in graph}
    # 初始化队列,将起始节点加入队列
    queue = deque([start_node])

    # 起始节点距离设为0,标记为已访问
    distance[start_node] = 0
    visited[start_node] = True

    # 队列不为空时循环处理
    while queue:
        # 取出队首节点
        current_node = queue.popleft()
        # 遍历当前节点的所有邻居
        for neighbor in graph[current_node]:
            # 如果邻居未被访问
            if not visited[neighbor]:
                # 标记邻居为已访问
                visited[neighbor] = True
                # 更新邻居节点距离(当前节点距离+1)
                distance[neighbor] = distance[current_node] + 1
                # 将邻居节点加入队列
                queue.append(neighbor)
    # 返回所有节点的最短距离
    return distance

def main():
    # 构建图
    graph = build_graph()
    # 执行BFS,获取最短距离
    shortest_distances = bfs_shortest_path('s', graph)
    # 输出结果
    print("从起点's'到各节点的最短距离:")
    for node, dist in shortest_distances.items():
        print(f"节点{node}: {dist}")

# 程序入口
if __name__ == "__main__":
    main()

一、建模场景与算法理论铺垫

1. 建模问题

给定无权无向连通图 ,求解单源最短路径 (从起点s到所有可达节点的边数最少路径)------这是数学建模中「图论路径规划问题」的经典子类。

2. BFS核心数学逻辑

BFS(广度优先搜索)的本质是层级遍历

  • 按「与起点的距离(边数)递增」的顺序访问节点;
  • 由于是无权图第一次访问某节点时的距离即为最短距离(若存在更短路径,BFS会先遍历更短路径的节点);
  • 时间复杂度为O(V+E)V为节点数,E为边数),是稀疏图的最优求解算法。
3. 图的表示

代码采用邻接表存储图:

  • 适合「稀疏图」(边数E远小于节点数V的平方),建模中90%以上的图问题都是稀疏图;
  • 结构为{节点: [邻居节点列表]},无向图中边s-a会同时出现在sa的邻居列表中(对称性)。

二、代码结构总览

代码分为3个核心模块,严格遵循数学建模流程

  1. build_graph():问题抽象→构建邻接表
  2. bfs_shortest_path():算法实现→BFS核心求解
  3. main():结果输出→调用流程+验证结果
  4. 程序入口:确保模块独立运行或导入复用

三、逐块/逐行深度解析

1. 依赖导入与图构建模块(build_graph()
python 复制代码
from collections import deque  # 导入双端队列,用于BFS的FIFO实现

def build_graph():
    # 构建邻接表表示图结构,键为节点,值为邻居节点列表
    graph = {
        's': ['a', 'b'],
        'a': ['s', 'c'],
        'b': ['s', 'd'],
        'c': ['a', 'd'],
        'd': ['b', 'c']
    }
    return graph
解析:
  • from collections import deque
    BFS需要**先进先出(FIFO)**的队列结构来保证层级遍历顺序。Python内置list.pop(0)O(n)时间复杂度(需移动所有元素),而deque.popleft()O(1)建模中处理大规模图时可避免超时
  • 邻接表graph
    100%还原题目给出的图结构:
    • 节点s的邻居:ab
    • 节点a的邻居:sc
    • 节点b的邻居:sd
    • 节点c的邻居:ad
    • 节点d的邻居:bc

2. BFS核心求解模块(bfs_shortest_path()

这是建模的核心算法实现,需结合数学逻辑逐行解析:

python 复制代码
def bfs_shortest_path(start_node, graph):
    # 初始化距离字典:-1表示不可达(未探索节点标记)
    distance = {node: -1 for node in graph}
    # 初始化访问标记字典:False=未访问,True=已访问(防止重复访问)
    visited = {node: False for node in graph}
    # 初始化BFS队列:用双端队列存储当前层级待处理节点
    queue = deque([start_node])

    # 起点边界条件:距离为0,标记为已访问
    distance[start_node] = 0
    visited[start_node] = True

    # 队列不为空时,循环处理当前层级的节点
    while queue:
        # 取出队首节点(FIFO,保证当前层级优先处理)
        current_node = queue.popleft()
        # 遍历当前节点的所有邻居(数学上的相邻节点集)
        for neighbor in graph[current_node]:
            # 若邻居未被访问→第一次到达,距离即为最短
            if not visited[neighbor]:
                # 标记邻居为已访问(纳入已探索节点集)
                visited[neighbor] = True
                # 核心数学逻辑:无权图中,邻居距离=当前节点距离+1
                distance[neighbor] = distance[current_node] + 1
                # 将邻居加入队列(作为下一层级待处理节点)
                queue.append(neighbor)
    # 返回所有节点的最短距离(建模结果)
    return distance
逐行/块数学+代码解析:
代码块 数学逻辑 编程作用
`distance = {node: -1 for node in graph}` 初始化**未探索节点集**的距离标记(-1=不可达) 记录起点到各节点的最短距离,初始化为"未知"
`visited = {node: False for node in graph}` 区分**已探索节点集**(True)和**未探索节点集**(False) 防止重复访问(BFS第一次访问即为最短距离,重复访问会导致错误)
`queue = deque(start_node)` 初始化**当前层级待处理节点队列** 用FIFO队列保证层级遍历顺序,核心结构保证
`distancestart_node = 0; visitedstart_node = True` 边界条件:起点到自身的距离为0,将起点转入**已探索节点集** BFS的起始点设置
`while queue:` 只要存在**当前层级待处理节点**,就继续遍历 BFS的主循环条件
`current_node = queue.popleft()` 取出**当前层级的第一个待处理节点**(FIFO) 当前探索的"层级中心",其距离已确定为最短
`for neighbor in graphcurrent_node` 遍历当前节点的**相邻节点集** 探索当前节点的所有可达邻居
`if not visitedneighbor` 检查邻居是否属于**未探索节点集** 确保是「第一次到达该邻居」,此时距离为最短
`visitedneighbor = True` 将邻居转入**已探索节点集** 防止后续重复访问
`distanceneighbor = distancecurrent_node + 1` 核心数学逻辑:无权图中,邻居的最短距离=当前节点距离+1 BFS能求最短路径的**根本依据**(层级递增)
`queue.append(neighbor)` 将邻居加入**下一层级待处理节点队列** 继续层级遍历

3. 建模流程调用模块(main()
python 复制代码
def main():
    # 步骤1:构建图(建模→问题抽象为图模型)
    graph = build_graph()
    # 步骤2:执行BFS(建模→算法求解)
    shortest_distances = bfs_shortest_path('s', graph)
    # 步骤3:输出结果(建模→结果验证与分析)
    print("从起点's'到各节点的最短距离:")
    for node, dist in shortest_distances.items():
        print(f"节点{node}: {dist}")

# 程序入口:只有直接运行模块时才执行main(),导入时不执行
if __name__ == "__main__":
    main()
解析:
  • 流程严格性:完全遵循数学建模的「问题抽象→模型构建→算法实现→结果输出」标准流程;
  • if __name__ == "__main__" :Python标准程序入口,确保代码可独立运行导入复用(如建模中需多次调用BFS算法时);
  • 结果输出 :清晰打印起点s到所有节点的最短距离,便于建模人员验证结果。

四、结果验证与数学逻辑对应

1. 手动计算最短距离(验证代码正确性)

根据题目图结构:

  • 起点s:距离=0(自身)
  • 直接邻居ab:距离=1(1条边)
  • 间接邻居cs→a→c)、ds→b→d):距离=2(2条边)
2. 代码输出结果
复制代码
从起点's'到各节点的最短距离:
节点s: 0
节点a: 1
节点b: 1
节点c: 2
节点d: 2

结果与手动计算完全一致,证明代码的正确性。


五、建模比赛中的扩展与优化

  1. 场景扩展
    • 若为加权图,将BFS修改为Dijkstra算法(用优先队列代替普通队列);
    • 若为有向图,仅需修改邻接表(只保留有向边的方向)。
  2. 输入扩展
    • 建模比赛中,图结构通常从外部文件(CSV、TXT)读入,可修改build_graph()函数从文件读取节点/边信息。
  3. 效率优化
    • collections.defaultdict简化邻接表构建;
    • numpy数组代替字典存储distancevisited(大规模图时速度提升10倍以上)。
  4. 功能扩展
    • 若需输出最短路径的具体节点序列 ,可添加predecessor字典(记录每个节点的前驱节点),回溯即可得到路径。

六、建模论文/讲解的重点提炼

若将此代码写入论文或向队友讲解,需突出:

  1. 理论依据:BFS求解无权图最短路径的数学原理(层级遍历+第一次访问即最短);
  2. 实现细节:邻接表的选择、双端队列的优化、访问标记的作用;
  3. 结果验证:手动计算与代码结果的一致性;
  4. 可扩展性:针对不同建模场景的扩展方案。
相关推荐
2601_953988071 天前
Ricon组态系统vs传统组态软件:为什么选择新一代Web组态平台
前端·后端·物联网·tcp/ip·数学建模·前端框架
数模竞赛Paid answer2 天前
2026年华东杯数学建模C题收益率预测问题解题全过程文档及程序
算法·数学建模·数据分析·华东杯
数模竞赛Paid answer3 天前
2026年电工杯数学建模A题绿电直连型电氢氨园区优化运行求解全过程论文及程序
算法·数学建模·电工杯
AI Dog3 天前
MathHub:数学建模学习社区,内置数学建模智能体Modulus
学习·数学建模·智能体·modulus·数学建模智能体
统计学小王子3 天前
分类模型评价指标——R语言(数学建模常用)
数学建模·分类·r语言
数模竞赛Paid answer3 天前
2026年华东杯数学建模B题医药物流安排问题解题全过程文档及程序
算法·数学建模·数据分析·华东杯
数模竞赛Paid answer4 天前
2020年深圳杯数学建模A题关于国家“先行示范区”建设中的医疗和养老保障问题解题全过程论文及程序
算法·数学建模·数据分析·深圳杯
数模竞赛Paid answer4 天前
2021年深圳杯数学建模C题配电网可靠性和故障软自愈研究解题全过程论文及程序
算法·数学建模·数据分析·深圳杯
数模竞赛Paid answer5 天前
2026年华数杯数学建模A题微构体中填充导电介质的仿真优化解题全过程及程序
数学建模·数据分析·华数杯
数模竞赛Paid answer5 天前
2021年深圳杯数学建模B题背向瑞利散射光纤激光传感器设计解题全过程论文及程序
数学建模·数据分析·深圳杯