最短路长度

边权为 1 的无向图最短路径求解:BFS 算法详解

在图论中,求解最短路径是一个基础且重要的问题。当图中所有边的权重都为 1 时,广度优先搜索(BFS)是解决这一问题的理想选择。本文将详细介绍如何使用 BFS 算法计算从顶点 1 到其他所有顶点的最短路径长度,并提供完整的实现代码。

问题分析

我们需要解决的问题是:

  • 给定一个边权为 1 的无向图
  • 计算从顶点 1 到其他所有顶点的最短路径长度
  • 如果某个顶点无法到达,则输出 - 1

输入格式:

  • 第一行包含两个整数 N 和 M,分别表示顶点数和边数
  • 接下来 M 行,每行包含两个整数 x 和 y,表示顶点 x 和 y 之间有一条边
  • 注意:图中可能存在自环(顶点到自身的边)和重边(同一对顶点间有多条边)

输出格式:

  • 共 N 行,第 i 行输出从顶点 1 到顶点 i 的最短路径长度

算法选择

为什么选择 BFS 算法?

  • BFS(广度优先搜索)的特性是按层次遍历图,先访问距离起点近的顶点,再访问距离远的顶点
  • 对于边权为 1 的图,BFS 第一次到达某个顶点时所经过的路径就是最短路径
  • BFS 的时间复杂度为 O (N+M),空间复杂度为 O (N+M),能够高效处理大规模图(如本题中 N 和 M 可达 10^6)

实现步骤详解

1. 头文件与命名空间

#include <iostream>   // 用于输入输出操作
#include <vector>     // 用于存储邻接表
#include <queue>      // 用于BFS的队列实现
using namespace std;  // 避免每次使用标准库都添加std::前缀

 

这些是 C++ 中处理图和 BFS 所需的基本库:

  • iostream:提供输入输出功能
  • vector:动态数组,适合存储邻接表
  • queue:队列数据结构,是 BFS 的核心

2. 输入输出优化

int main() {
    // 关闭同步,加速输入输出
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

 

这两行代码是处理大规模输入输出时的关键优化:

  • ios::sync_with_stdio(false):解除 C++ 标准输入输出与 C 标准输入输出的同步
  • cin.tie(nullptr):解除 cin 与 cout 的绑定
  • 这两项优化可以显著提高输入输出速度,对于 10^6 级别的数据处理尤为重要

3. 读取图的基本信息

    int N, M;
    cin >> N >> M;  // 读取顶点数和边数

 

  • N:表示图中顶点的数量(顶点编号从 1 到 N)
  • M:表示图中边的数量

4. 构建邻接表

    // 构建邻接表表示图,顶点编号从1开始,所以大小为N+1
    vector<vector<int>> adj(N + 1);
    for (int i = 0; i < M; ++i) {
        int x, y;
        cin >> x >> y;
        adj[x].push_back(y);  // x连接到y
        adj[y].push_back(x);  // 无向图,y也连接到x
    }

 

邻接表是表示图的高效方式:

  • adj是一个 vector 的 vector,adj[u]存储了所有与 u 直接相连的顶点
  • 由于是无向图,每条边 (x,y) 需要双向添加到邻接表中
  • 对于自环(x=y),代码会自动处理,不影响最终结果
  • 对于重边,虽然会被多次添加到邻接表,但 BFS 只会处理一次,不影响结果正确性

 

5. 初始化距离数组和队列

    // 距离数组,-1表示不可达
    vector<int> dist(N + 1, -1);
    queue<int> q;  // BFS使用的队列
    
    // 起点是1,距离为0
    dist[1] = 0;
    q.push(1);  // 将起点加入队列

 

  • dist数组:dist[i]表示从顶点 1 到顶点 i 的最短距离
    • 初始值设为 - 1,表示所有顶点都不可达
    • 起点 1 的距离设为 0
  • q队列:用于 BFS 过程中存储待处理的顶点,首先将起点 1 加入队列

6. BFS 核心算法

    // BFS计算最短路径
    while (!q.empty()) {  // 队列不为空时继续
        int u = q.front();  // 取出队首顶点
        q.pop();
        
        // 遍历u的所有邻居
        for (int v : adj[u]) {
            // 如果v未被访问过(距离为-1)
            if (dist[v] == -1) {
                dist[v] = dist[u] + 1;  // 距离是u的距离+1(边权为1)
                q.push(v);  // 将v加入队列,继续探索
            }
        }
    }

 

BFS 算法的核心逻辑:

  1. 当队列不为空时,取出队首顶点 u
  2. 遍历 u 的所有邻居 v
  3. 对于尚未访问的邻居 v(dist[v] == -1):
    • 设置 v 的距离为 u 的距离 + 1(因为边权为 1)
    • 将 v 加入队列,以便后续处理其邻居
  4. 重复以上步骤直到队列为空

BFS 的特性保证了每个顶点只被访问一次,且第一次被访问时的距离就是最短距离。

7. 输出结果

    // 输出从顶点1到每个顶点的最短距离
    for (int i = 1; i <= N; ++i) {
        cout << dist[i] << '\n';
    }
    
    return 0;
}

 

按顶点编号从 1 到 N 的顺序,输出每个顶点到起点 1 的最短距离。对于无法到达的顶点,会输出初始值 - 1。

完整代码

 

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int main() {
    // 关闭同步,加速输入输出
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int N, M;
    cin >> N >> M;  // 读取顶点数和边数
    
    // 构建邻接表表示图,顶点编号从1开始,所以大小为N+1
    vector<vector<int>> adj(N + 1);
    for (int i = 0; i < M; ++i) {
        int x, y;
        cin >> x >> y;
        adj[x].push_back(y);  // x连接到y
        adj[y].push_back(x);  // 无向图,y也连接到x
    }
    
    // 距离数组,-1表示不可达
    vector<int> dist(N + 1, -1);
    queue<int> q;  // BFS使用的队列
    
    // 起点是1,距离为0
    dist[1] = 0;
    q.push(1);  // 将起点加入队列
    
    // BFS计算最短路径
    while (!q.empty()) {  // 队列不为空时继续
        int u = q.front();  // 取出队首顶点
        q.pop();
        
        // 遍历u的所有邻居
        for (int v : adj[u]) {
            // 如果v未被访问过(距离为-1)
            if (dist[v] == -1) {
                dist[v] = dist[u] + 1;  // 距离是u的距离+1(边权为1)
                q.push(v);  // 将v加入队列,继续探索
            }
        }
    }
    
    // 输出从顶点1到每个顶点的最短距离
    for (int i = 1; i <= N; ++i) {
        cout << dist[i] << '\n';
    }
    
    return 0;
}

 

示例解析

以题目中给出的示例输入为例:

 

5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5

 

这个图包含 5 个顶点和 7 条边,其结构如下:

 

  • 顶点 1 与顶点 2、3 相连
  • 顶点 2 与顶点 1、4、3 相连
  • 顶点 3 与顶点 1、4、2 相连
  • 顶点 4 与顶点 2、3、5 相连(有两条边连接 4 和 5)
  • 顶点 5 与顶点 4 相连

BFS 的执行过程:

  1. 初始队列:[1],dist [1]=0
  2. 处理顶点 1:将 2 和 3 加入队列,dist [2]=1,dist [3]=1,队列变为 [2,3]
  3. 处理顶点 2:将 4 加入队列,dist [4]=2,队列变为 [3,4]
  4. 处理顶点 3:4 已被访问,队列变为 [4]
  5. 处理顶点 4:将 5 加入队列,dist [5]=3,队列变为 [5]
  6. 处理顶点 5:没有未访问的邻居,队列变为空

最终输出结果:

 

0  // 顶点1到自身的距离
1  // 顶点1到顶点2的距离
1  // 顶点1到顶点3的距离
2  // 顶点1到顶点4的距离
3  // 顶点1到顶点5的距离

 

复杂度分析

  • 时间复杂度:O (N + M)

    • 每个顶点最多入队一次,出队一次:O (N)
    • 每条边最多被处理两次(无向图):O (M)
  • 空间复杂度:O (N + M)

    • 邻接表存储:O (M)
    • 距离数组:O (N)
    • 队列存储:最坏情况下 O (N)

这种复杂度使得算法能够高效处理 10^6 规模的图数据。

 

总结

本文详细介绍了使用 BFS 算法求解边权为 1 的无向图最短路径问题:

 

  1. BFS 算法特别适合处理边权为 1 的图的最短路径问题
  2. 邻接表是表示图的高效数据结构
  3. 输入输出优化对于处理大规模数据至关重要
  4. 算法能够自动处理自环和重边等特殊情况

 

通过本文的学习,你应该能够理解 BFS 算法的工作原理及其在最短路径问题中的应用,并能够独立实现相应的代码解决类似问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值