Java 实现无向图所有连通分量查找算法

一、项目背景详细介绍

在网络分析、社交媒体、地理信息、物联网等领域,图(Graph)作为实体及其关系的建模工具,被广泛使用。无向图中,连通分量(Connected Component)是指任意两个顶点之间都存在路径的最大顶点子集。查找图的所有连通分量可以帮助:

  • 识别社交网络中的社区或群组;

  • 分析道路网络的孤岛区域;

  • 检测系统拓扑结构中的子网;

  • 预处理为后续算法(如最短路径、最小生成树)分配初始分量。

本项目将使用 Java 语言,以邻接表存储无向图,基于 DFS 和 BFS 两种方法分别实现所有连通分量的查找,并输出各分量的顶点列表。


二、项目需求详细介绍

  1. 输入格式

    • 从控制台读取顶点数 n、边数 m

    • 读取 m 条无向边 u v,顶点编号 0 ≤ u,v < n

  2. 功能实现

    • 构建邻接表 List<List<Integer>> graph

    • DFS 版本:递归或显式栈实现 dfs(int u, List<Integer> comp, boolean[] visited)

    • BFS 版本:队列实现 bfs(int start, List<Integer> comp, boolean[] visited)

    • 主方法遍历所有顶点,若未访问则调用 DFS/BFS 获取一个分量,记录并继续。

  3. 输出要求

    • 打印每个分量的顶点列表,格式:Component 1: [0,2,5]

    • 打印分量总数。

  4. 性能与规模

    • 支持 n,m 可达 10^5,邻接表遍历总耗时 O(n + m);

  5. 可扩展性

    • 提供 DFS 与 BFS 两种实现方法供对比;

    • 注释清晰,适合教学与集成。


三、相关技术详细介绍

  1. 邻接表存储

    • List<List<Integer>> graph = new ArrayList<>(n),空间 O(n + m);

  2. DFS 查分量

    • 使用递归或栈,时间 O(size of component + edges);

  3. BFS 查分量

    • 使用 Queue<Integer>,层次遍历,同样时间 O(n + m);

  4. 访问标记

    • 维护 boolean[] visited,确保每个节点仅加入一个分量;


四、实现思路详细介绍

  1. 图构建

    List<List<Integer>> graph = new ArrayList<>(n);
    for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
    for (edges) {
        graph.get(u).add(v);
        graph.get(v).add(u);
    }
    
  2. DFS 版本

    void dfs(int u, List<Integer> comp, boolean[] visited) {
        visited[u] = true; comp.add(u);
        for (int v : graph.get(u)) if (!visited[v]) dfs(v,comp,visited);
    }
    
  3. BFS 版本

    void bfs(int start, List<Integer> comp, boolean[] visited) {
        Queue<Integer> q = new ArrayDeque<>(); q.offer(start); visited[start]=true;
        while (!q.isEmpty()) {
            int u=q.poll(); comp.add(u);
            for (int v:graph.get(u)) if (!visited[v]) { visited[v]=true; q.offer(v);} }
    }
    
  4. 主流程

    Arrays.fill(visited,false);
    int count=0;
    for(int i=0;i<n;i++){
        if(!visited[i]){
            List<Integer> comp=new ArrayList<>();
            // dfs(i,comp,visited); 或 bfs(i,comp,visited);
            print comp; count++;
        }
    }
    System.out.println("Total components="+count);
    

五、完整实现源码

import java.util.*;

public class ConnectedComponents {
    private int n;
    private List<List<Integer>> graph;
    private boolean[] visited;

    public ConnectedComponents(int n) {
        this.n = n;
        graph = new ArrayList<>(n);
        for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
        visited = new boolean[n];
    }

    public void addEdge(int u, int v) {
        graph.get(u).add(v);
        graph.get(v).add(u);
    }

    // DFS 版本
    private void dfs(int u, List<Integer> comp) {
        visited[u] = true;
        comp.add(u);
        for (int v : graph.get(u)) {
            if (!visited[v]) dfs(v, comp);
        }
    }

    // BFS 版本
    private void bfs(int start, List<Integer> comp) {
        Deque<Integer> queue = new ArrayDeque<>();
        queue.offer(start);
        visited[start] = true;
        while (!queue.isEmpty()) {
            int u = queue.poll();
            comp.add(u);
            for (int v : graph.get(u)) {
                if (!visited[v]) {
                    visited[v] = true;
                    queue.offer(v);
                }
            }
        }
    }

    public void findAndPrintComponents(boolean useDFS) {
        Arrays.fill(visited, false);
        int count = 0;
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                List<Integer> comp = new ArrayList<>();
                if (useDFS) dfs(i, comp);
                else bfs(i, comp);
                System.out.println("Component " + (++count) + ": " + comp);
            }
        }
        System.out.println("Total components = " + count);
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入 n m:");
        int n = sc.nextInt(), m = sc.nextInt();
        ConnectedComponents cc = new ConnectedComponents(n);
        System.out.println("请输入 m 条边 u v:");
        for (int i = 0; i < m; i++) cc.addEdge(sc.nextInt(), sc.nextInt());

        System.out.println("使用 DFS 查找连通分量:");
        cc.findAndPrintComponents(true);

        System.out.println("使用 BFS 查找连通分量:");
        cc.findAndPrintComponents(false);
    }
}

六、代码详细解读(只说明方法作用)

  • dfs:递归深度优先遍历一个连通分量;

  • bfs:广度优先遍历一个连通分量;

  • findAndPrintComponents:主流程,迭代所有顶点调用 DFS/BFS 并打印结果;

  • main:读取输入并演示两种方法。


七、项目详细总结

本文提供了基于 DFS/BFS 的两种连通分量查找算法,均具有线性时间复杂度 O(n + m),适用于大规模图的预处理与社区检测。


八、常见问题及解答

  1. Q:DFS 与 BFS 优劣?

    • A:时间复杂度相同,DFS 更简洁,BFS 更利于层次遍历;

  2. Q:图不连通时如何处理?

    • A:主流程遍历所有未访问顶点,自动分离分量;

  3. Q:递归深度过大?

    • A:可启用显式栈或增加 JVM 栈大小;

  4. Q:无向图平行边?

    • A:会导致多次遍历同一邻居,但 visited 阻止重复进入子递归;


九、扩展方向与性能优化

  1. 显式栈版本 DFS:避免深度递归;

  2. 并行分量查找:多线程处理不同起点;

  3. 组件索引结构:输出每个顶点所属分量编号;

  4. 图可视化:动态展示分量划分;

  5. 应用集成:同最短路径、最小生成树等算法组合使用。


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐