图论(3)单源无权最短路径算法实现(BFS实现)

发布于:2025-08-12 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

一 BFS算法实现思路

1 算法核心思想

2 算法步骤

3 算法特点

二 代码实现(Java)


在图论中,单源无权最短路径问题是指在一个无权图(所有边权重相等,通常视为1)中,从某个源顶点出发,计算到图中所有其他顶点的最短路径的问题。这类问题在计算机科学和现实应用中有着广泛的应用:

  • 关键概念

    1. 无权图:所有边具有相同的权重(通常视为1)

    2. 最短路径:连接两个顶点的边数最少的路径

    3. 源顶点:算法计算的起点

    4. 距离:从源顶点到目标顶点的最短路径长度

    5. 前驱节点:在最短路径上,某个顶点的前一个顶点

一 BFS算法实现思路

广度优先搜索(BFS)是解决单源无权最短路径问题的理想算法,因为它按层级遍历图结构,天然保证了最先到达的路径就是最短路径。

1 算法核心思想

  1. 层级遍历:从源顶点开始,首先访问所有距离为1的顶点,然后是距离为2的顶点,以此类推

  2. 队列管理:使用队列存储待访问的顶点,确保先进先出(FIFO)的访问顺序

  3. 距离标记:为每个顶点记录从源顶点到该顶点的距离

  4. 路径追踪:记录最短路径上的前驱顶点,便于重构完整路径

2 算法步骤

  1. 初始化源顶点距离为0,其他顶点距离为无穷大(∞)

  2. 将源顶点加入队列

  3. 当队列不为空时:

    • 从队列中取出一个顶点v

    • 遍历v的所有邻接顶点w:

      • 如果w未被访问过(距离为∞):

        • 设置w的距离为v的距离+1

        • 设置w的前驱为v

        • 将w加入队列

  4. 当队列为空时,算法结束

3 算法特点

  • 时间复杂度:O(V+E),其中V是顶点数,E是边数

  • 空间复杂度:O(V),用于存储队列和顶点信息

  • 完整性:能够找到源顶点到所有可达顶点的最短路径

  • 最优性:保证找到的路径是最短的

二 代码实现(Java)

import java.util.*;
​
class Vertex {
    public String name;          // 顶点名称
    public List<Vertex> adj;    // 邻接顶点列表
    public int dist = Integer.MAX_VALUE;  // 初始距离设为无穷大
    public Vertex path;         // 最短路径上的前驱顶点
​
    public Vertex(String name) {
        this.name = name;
        this.adj = new ArrayList<>();
    }
    
    // 添加邻接顶点
    public void addNeighbor(Vertex... neighbors) {
        for (Vertex neighbor : neighbors) {
            this.adj.add(neighbor);
        }
    }
}
​
public class UnweightedShortestPath {
    public static final int INFINITY = Integer.MAX_VALUE;
​
    /**
     * 计算单源无权最短路径
     * @param s 源顶点
     */
    public static void unweighted(Vertex s) {
        // 初始化队列
        Queue<Vertex> q = new LinkedList<>();
        
        // 初始化源顶点
        s.dist = 0;             // 源点距离设为0
        q.add(s);               // 源点入队
        
        while (!q.isEmpty()) {
            Vertex v = q.remove();
            
            // 遍历所有邻接顶点
            for (Vertex w : v.adj) {
                // 如果顶点未被访问过
                if (w.dist == INFINITY) {
                    w.dist = v.dist + 1;    // 更新距离
                    w.path = v;             // 记录路径
                    q.add(w);               // 邻接点入队
                }
            }
        }
    }
    
    /**
     * 重构从源点到目标顶点的最短路径
     * @param target 目标顶点
     * @return 路径顶点列表
     */
    public static List<Vertex> reconstructPath(Vertex target) {
        List<Vertex> path = new ArrayList<>();
        // 如果目标顶点不可达
        if (target.dist == INFINITY) {
            return path;
        }
        
        // 从目标顶点回溯到源顶点
        for (Vertex v = target; v != null; v = v.path) {
            path.add(v);
        }
        
        // 反转路径:从源顶点到目标顶点
        Collections.reverse(path);
        return path;
    }
    
    /**
     * 打印最短路径结果
     * @param vertices 顶点列表
     */
    public static void printResults(List<Vertex> vertices) {
        System.out.println("顶点\t距离\t前驱\t路径");
        System.out.println("--------------------------------------");
        
        for (Vertex v : vertices) {
            System.out.printf("%s\t%d\t%s\t",
                v.name,
                v.dist,
                (v.path != null) ? v.path.name : "null");
            
            // 打印路径
            List<Vertex> path = reconstructPath(v);
            if (path.isEmpty()) {
                System.out.print("不可达");
            } else {
                for (int i = 0; i < path.size(); i++) {
                    if (i > 0) System.out.print(" → ");
                    System.out.print(path.get(i).name);
                }
            }
            System.out.println();
        }
    }
​
    // 测试用例
    public static void main(String[] args) {
        // 创建顶点
        Vertex v1 = new Vertex("图书馆");
        Vertex v2 = new Vertex("教学楼A");
        Vertex v3 = new Vertex("教学楼B");
        Vertex v4 = new Vertex("食堂");
        Vertex v5 = new Vertex("体育馆");
        Vertex v6 = new Vertex("宿舍");
        Vertex v7 = new Vertex("校门");
        
        // 构建校园地图的无向图
        v1.addNeighbor(v2, v4);    // 图书馆连接教学楼A和食堂
        v2.addNeighbor(v1, v3, v5); // 教学楼A连接图书馆、教学楼B和体育馆
        v3.addNeighbor(v2, v6);     // 教学楼B连接教学楼A和宿舍
        v4.addNeighbor(v1, v5, v7); // 食堂连接图书馆、体育馆和校门
        v5.addNeighbor(v2, v4, v6); // 体育馆连接教学楼A、食堂和宿舍
        v6.addNeighbor(v3, v5, v7); // 宿舍连接教学楼B、体育馆和校门
        v7.addNeighbor(v4, v6);     // 校门连接食堂和宿舍
        
        List<Vertex> campus = Arrays.asList(v1, v2, v3, v4, v5, v6, v7);
        
        // 从图书馆出发计算最短路径
        unweighted(v1);
        
        // 打印结果
        printResults(campus);
        
        // 额外测试:从图书馆到校门的最短路径
        System.out.println("\n从图书馆到校门的最短路径:");
        List<Vertex> pathToGate = reconstructPath(v7);
        for (Vertex v : pathToGate) {
            System.out.print(v.name + (v != v7 ? " → " : ""));
        }
    }
}
  • 测试结果分析

运行上述程序,我们可以得到以下结果:

顶点  距离  前驱  路径
--------------------------------------
图书馆 0   null    图书馆
教学楼A    1   图书馆 图书馆 → 教学楼A
教学楼B    2   教学楼A    图书馆 → 教学楼A → 教学楼B
食堂  1   图书馆 图书馆 → 食堂
体育馆 2   教学楼A    图书馆 → 教学楼A → 体育馆
宿舍  3   教学楼B    图书馆 → 教学楼A → 教学楼B → 宿舍
校门  2   食堂  图书馆 → 食堂 → 校门
​
从图书馆到校门的最短路径:
图书馆 → 食堂 → 校门

BFS算法是解决单源无权最短路径问题的最佳选择,它利用队列按层级遍历图结构,保证最先找到的路径就是最短路径。本文介绍了算法的理论基础、实现思路,并提供了完整的Java实现代码。该算法在网络路由、社交网络分析、游戏开发等领域有广泛应用,是图论中最基础且重要的算法之一。