图论基础算法:DFS、BFS、并查集与拓扑排序的Java实现

图论基础算法:DFS、BFS、并查集与拓扑排序的Java实现

图论是计算机科学中的核心领域,广泛应用于社交网络分析、路径规划、编译器设计等场景。本文将使用Java实现图论中的基础算法,包括深度优先搜索(DFS)、广度优先搜索(BFS)、并查集(Union-Find)和拓扑排序(Topological Sorting)。

图的表示方法

邻接表表示法

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

java 复制代码
import java.util.*;

public class GraphAlgorithms {
    
    // 创建邻接表
    public static List<List<Integer>> createGraph(int vertices) {
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < vertices; i++) {
            graph.add(new LinkedList<>());
        }
        return graph;
    }
    
    // 添加无向边
    public static void addUndirectedEdge(List<List<Integer>> graph, int v, int w) {
        graph.get(v).add(w);
        graph.get(w).add(v);
    }
    
    // 添加有向边
    public static void addDirectedEdge(List<List<Integer>> graph, int v, int w) {
        graph.get(v).add(w);
    }
}

邻接矩阵表示法

邻接矩阵适合表示稠密图:

java 复制代码
// 创建邻接矩阵
public static boolean[][] createAdjMatrix(int vertices) {
    return new boolean[vertices][vertices];
}

// 添加边
public static void addMatrixEdge(boolean[][] matrix, int v, int w) {
    matrix[v][w] = true;
    matrix[w][v] = true; // 无向图
}

深度优先搜索(DFS)

DFS以深度优先方式遍历图:

java 复制代码
// 迭代实现DFS
public static void dfsIterative(List<List<Integer>> graph, int start) {
    boolean[] visited = new boolean[graph.size()];
    Stack<Integer> stack = new Stack<>();
    
    stack.push(start);
    visited[start] = true;
    
    while (!stack.isEmpty()) {
        int current = stack.pop();
        System.out.print(current + " ");
        
        for (int neighbor : graph.get(current)) {
            if (!visited[neighbor]) {
                stack.push(neighbor);
                visited[neighbor] = true;
            }
        }
    }
    System.out.println();
}

// 递归实现DFS
public static void dfsRecursive(List<List<Integer>> graph, int start) {
    boolean[] visited = new boolean[graph.size()];
    dfsRecursiveUtil(graph, visited, start);
    System.out.println();
}

private static void dfsRecursiveUtil(List<List<Integer>> graph, 
                                    boolean[] visited, int current) {
    visited[current] = true;
    System.out.print(current + " ");
    
    for (int neighbor : graph.get(current)) {
        if (!visited[neighbor]) {
            dfsRecursiveUtil(graph, visited, neighbor);
        }
    }
}

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

广度优先搜索(BFS)

BFS按层次遍历图:

java 复制代码
public static void bfs(List<List<Integer>> graph, int start) {
    boolean[] visited = new boolean[graph.size()];
    Queue<Integer> queue = new LinkedList<>();
    
    queue.add(start);
    visited[start] = true;
    
    while (!queue.isEmpty()) {
        int current = queue.poll();
        System.out.print(current + " ");
        
        for (int neighbor : graph.get(current)) {
            if (!visited[neighbor]) {
                queue.add(neighbor);
                visited[neighbor] = true;
            }
        }
    }
    System.out.println();
}

应用场景:社交网络中的好友推荐、最短路径查找

并查集(Union-Find)

并查集高效处理不相交集合的合并与查询问题:

java 复制代码
static class UnionFind {
    int[] parent;
    int[] rank;
    
    public UnionFind(int size) {
        parent = new int[size];
        rank = new int[size];
        for (int i = 0; i < size; i++) {
            parent[i] = i;
        }
    }
    
    // 查找操作(带路径压缩)
    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // 路径压缩
        }
        return parent[x];
    }
    
    // 合并操作(带按秩合并)
    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        
        if (rootX == rootY) return;
        
        if (rank[rootX] > rank[rootY]) {
            parent[rootY] = rootX;
        } else if (rank[rootX] < rank[rootY]) {
            parent[rootX] = rootY;
        } else {
            parent[rootY] = rootX;
            rank[rootX]++;
        }
    }
}

优化技术

  • 路径压缩:使树更平坦,加速后续查找
  • 按秩合并:防止树过高,保持平衡

拓扑排序

拓扑排序对有向无环图(DAG)进行线性排序:

java 复制代码
public static void topologicalSort(List<List<Integer>> graph) {
    int n = graph.size();
    int[] inDegree = new int[n];
    Queue<Integer> queue = new LinkedList<>();
    List<Integer> result = new ArrayList<>();
    
    // 计算入度
    for (int i = 0; i < n; i++) {
        for (int neighbor : graph.get(i)) {
            inDegree[neighbor]++;
        }
    }
    
    // 将入度为0的节点加入队列
    for (int i = 0; i < n; i++) {
        if (inDegree[i] == 0) {
            queue.add(i);
        }
    }
    
    // 处理队列
    while (!queue.isEmpty()) {
        int current = queue.poll();
        result.add(current);
        
        for (int neighbor : graph.get(current)) {
            if (--inDegree[neighbor] == 0) {
                queue.add(neighbor);
            }
        }
    }
    
    // 检测环
    if (result.size() != n) {
        System.out.println("Graph contains a cycle!");
    } else {
        for (int node : result) {
            System.out.print(node + " ");
        }
        System.out.println();
    }
}

应用场景

  1. 任务调度:确定任务执行顺序
  2. 课程安排:解决课程依赖关系
  3. 编译顺序:确定源代码编译顺序

完整示例与测试

java 复制代码
public static void main(String[] args) {
    // 创建无向图
    List<List<Integer>> graph = createGraph(6);
    addUndirectedEdge(graph, 0, 1);
    addUndirectedEdge(graph, 0, 2);
    addUndirectedEdge(graph, 1, 3);
    addUndirectedEdge(graph, 1, 4);
    addUndirectedEdge(graph, 2, 4);
    addUndirectedEdge(graph, 3, 5);
    addUndirectedEdge(graph, 4, 5);
    
    System.out.print("DFS (iterative): ");
    dfsIterative(graph, 0);
    
    System.out.print("DFS (recursive): ");
    dfsRecursive(graph, 0);
    
    System.out.print("BFS: ");
    bfs(graph, 0);
    
    // 并查集测试
    UnionFind uf = new UnionFind(5);
    uf.union(0, 1);
    uf.union(2, 3);
    uf.union(1, 4);
    uf.union(3, 4);
    
    System.out.print("Union-Find sets: ");
    for (int i = 0; i < 5; i++) {
        System.out.print(uf.find(i) + " ");
    }
    System.out.println();
    
    // 创建有向无环图
    List<List<Integer>> dag = createGraph(6);
    addDirectedEdge(dag, 2, 3);
    addDirectedEdge(dag, 3, 1);
    addDirectedEdge(dag, 4, 0);
    addDirectedEdge(dag, 4, 1);
    addDirectedEdge(dag, 5, 0);
    addDirectedEdge(dag, 5, 2);
    
    System.out.print("Topological sort: ");
    topologicalSort(dag);
    
    // 测试有环图
    List<List<Integer>> cyclicGraph = createGraph(3);
    addDirectedEdge(cyclicGraph, 0, 1);
    addDirectedEdge(cyclicGraph, 1, 2);
    addDirectedEdge(cyclicGraph, 2, 0);
    
    System.out.print("Cyclic graph test: ");
    topologicalSort(cyclicGraph);
}

输出示例

复制代码
DFS (iterative): 0 2 4 5 3 1 
DFS (recursive): 0 1 3 5 4 2 
BFS: 0 1 2 3 4 5 
Union-Find sets: 1 1 3 1 1 
Topological sort: 4 5 0 2 3 1 
Cyclic graph test: Graph contains a cycle!

算法比较与选择指南

算法 时间复杂度 空间复杂度 最佳适用场景
DFS O(V+E) O(V) 连通性检测、拓扑排序(递归)
BFS O(V+E) O(V) 最短路径、层级遍历
并查集 接近O(1) O(V) 动态连通性问题
拓扑排序 O(V+E) O(V) 任务调度、依赖分析

使用建议

  1. 需要最短路径时优先选择BFS
  2. 处理动态连通性问题使用并查集
  3. 任务调度和依赖分析使用拓扑排序
  4. 一般遍历和连通性检测DFS和BFS均可

Java实现特点

  1. 集合框架 :使用ArrayListLinkedList等集合类简化实现
  2. 泛型:使用泛型确保类型安全
  3. 面向对象:并查集使用静态内部类封装
  4. 异常处理:Java自带异常处理机制增强健壮性
  5. 内存管理:Java自动垃圾回收简化内存管理

总结

本文使用Java实现了图论中四种基础算法:

  1. DFS:深度优先遍历,提供递归和迭代两种实现
  2. BFS:广度优先遍历,使用队列实现层级遍历
  3. 并查集:高效处理集合合并与查询,使用路径压缩和按秩合并优化
  4. 拓扑排序:Kahn算法实现,自动检测环

所有实现均遵循Java最佳实践,代码简洁高效。这些算法构成了图论的基础,掌握它们对于解决更复杂的图论问题至关重要。

相关推荐
Hellyc2 小时前
基于模板设计模式开发优惠券推送功能以及对过期优惠卷进行定时清理
java·数据库·设计模式·rocketmq
lifallen2 小时前
Paimon LSM Tree Compaction 策略
java·大数据·数据结构·数据库·算法·lsm-tree
hdsoft_huge2 小时前
SpringBoot 与 JPA 整合全解析:架构优势、应用场景、集成指南与最佳实践
java·spring boot·架构
百锦再3 小时前
详细解析 .NET 依赖注入的三种生命周期模式
java·开发语言·.net·di·注入·模式·依赖
程序员的世界你不懂3 小时前
基于Java+Maven+Testng+Selenium+Log4j+Allure+Jenkins搭建一个WebUI自动化框架(2)对框架加入业务逻辑层
java·selenium·maven
web_Hsir4 小时前
vue3.2 前端动态分页算法
前端·算法
有没有没有重复的名字4 小时前
线程安全的单例模式与读者写者问题
java·开发语言·单例模式
程序员的世界你不懂6 小时前
基于Java+Maven+Testng+Selenium+Log4j+Allure+Jenkins搭建一个WebUI自动化框架(4)集成Allure报表
java·selenium·maven
isNotNullX6 小时前
数据中台架构解析:湖仓一体的实战设计
java·大数据·数据库·架构·spark
皮皮林5516 小时前
“RPC好,还是RESTful好?”,这个问题不简单
java