四大图算法 Java 实现详解

四大图算法 Java 实现详解

以下是 Dijkstra、Floyd-Warshall、Kruskal 和 Prim 四大图算法的完整 Java 实现,包含详细注释和应用场景分析:

import java.util.*;

public class GraphAlgorithms {

    // 1. Dijkstra 算法 - 单源最短路径
    public static void dijkstra(int[][] graph, int start) {
        int n = graph.length;
        int[] dist = new int[n];          // 存储最短距离
        boolean[] visited = new boolean[n]; // 标记已访问节点
        Arrays.fill(dist, Integer.MAX_VALUE);
        dist[start] = 0;

        for (int i = 0; i < n - 1; i++) {
            // 找到未访问节点中距离最小的节点
            int u = minDistance(dist, visited);
            visited[u] = true;

            // 更新邻接节点的距离
            for (int v = 0; v < n; v++) {
                if (!visited[v] && graph[u][v] != 0 && 
                    dist[u] != Integer.MAX_VALUE && 
                    dist[u] + graph[u][v] < dist[v]) {
                    dist[v] = dist[u] + graph[u][v];
                }
            }
        }

        // 打印结果
        System.out.println("Dijkstra 最短路径 (起点 " + start + "):");
        for (int i = 0; i < n; i++) {
            System.out.println("到节点 " + i + " 的最短距离: " + dist[i]);
        }
    }

    private static int minDistance(int[] dist, boolean[] visited) {
        int min = Integer.MAX_VALUE, minIndex = -1;
        for (int v = 0; v < dist.length; v++) {
            if (!visited[v] && dist[v] <= min) {
                min = dist[v];
                minIndex = v;
            }
        }
        return minIndex;
    }

    // 2. Floyd-Warshall 算法 - 所有节点对最短路径
    public static void floydWarshall(int[][] graph) {
        int n = graph.length;
        int[][] dist = new int[n][n];
        
        // 初始化距离矩阵
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                dist[i][j] = graph[i][j];
                if (i != j && dist[i][j] == 0) {
                    dist[i][j] = 9999; // 表示无穷大
                }
            }
        }

        // 三重循环计算所有节点对的最短路径
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (dist[i][k] + dist[k][j] < dist[i][j]) {
                        dist[i][j] = dist[i][k] + dist[k][j];
                    }
                }
            }
        }

        // 打印结果
        System.out.println("\nFloyd-Warshall 所有节点对最短路径:");
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (dist[i][j] == 9999) System.out.print("INF ");
                else System.out.print(dist[i][j] + " ");
            }
            System.out.println();
        }
    }

    // 3. Kruskal 算法 - 最小生成树(基于并查集)
    static class Edge implements Comparable<Edge> {
        int src, dest, weight;
        
        public Edge(int src, int dest, int weight) {
            this.src = src;
            this.dest = dest;
            this.weight = weight;
        }
        
        @Override
        public int compareTo(Edge other) {
            return this.weight - other.weight;
        }
    }

    static class Subset {
        int parent, rank;
    }

    public static void kruskalMST(int V, List<Edge> edges) {
        // 按权重排序所有边
        Collections.sort(edges);
        
        Edge[] result = new Edge[V - 1]; // 存储最小生成树的边
        Subset[] subsets = new Subset[V];
        
        // 初始化并查集
        for (int i = 0; i < V; i++) {
            subsets[i] = new Subset();
            subsets[i].parent = i;
            subsets[i].rank = 0;
        }
        
        int e = 0; // 已选边计数
        int i = 0; // 已处理边索引
        
        while (e < V - 1 && i < edges.size()) {
            Edge next = edges.get(i++);
            
            int x = find(subsets, next.src);
            int y = find(subsets, next.dest);
            
            // 如果不形成环,则加入结果集
            if (x != y) {
                result[e++] = next;
                union(subsets, x, y);
            }
        }
        
        // 打印最小生成树
        System.out.println("\nKruskal 最小生成树:");
        int minCost = 0;
        for (i = 0; i < e; i++) {
            System.out.println(result[i].src + " - " + result[i].dest + " : " + result[i].weight);
            minCost += result[i].weight;
        }
        System.out.println("最小生成树总权重: " + minCost);
    }
    
    private static int find(Subset[] subsets, int i) {
        if (subsets[i].parent != i) {
            subsets[i].parent = find(subsets, subsets[i].parent);
        }
        return subsets[i].parent;
    }
    
    private static void union(Subset[] subsets, int x, int y) {
        int rootX = find(subsets, x);
        int rootY = find(subsets, y);
        
        // 按秩合并
        if (subsets[rootX].rank < subsets[rootY].rank) {
            subsets[rootX].parent = rootY;
        } else if (subsets[rootX].rank > subsets[rootY].rank) {
            subsets[rootY].parent = rootX;
        } else {
            subsets[rootY].parent = rootX;
            subsets[rootX].rank++;
        }
    }

    // 4. Prim 算法 - 最小生成树(基于优先队列)
    public static void primMST(int[][] graph) {
        int V = graph.length;
        int[] parent = new int[V]; // 存储MST中节点的父节点
        int[] key = new int[V];    // 存储节点的键值(到MST的最小边权重)
        boolean[] inMST = new boolean[V]; // 标记节点是否在MST中
        
        Arrays.fill(key, Integer.MAX_VALUE);
        key[0] = 0; // 从节点0开始
        parent[0] = -1; // 根节点没有父节点
        
        // 使用优先队列优化选择最小键值节点的过程
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
        pq.add(new int[]{0, key[0]});
        
        while (!pq.isEmpty()) {
            int u = pq.poll()[0];
            inMST[u] = true;
            
            // 遍历所有邻接节点
            for (int v = 0; v < V; v++) {
                // 如果存在边、节点未加入MST、且权重小于当前键值
                if (graph[u][v] != 0 && !inMST[v] && graph[u][v] < key[v]) {
                    parent[v] = u;
                    key[v] = graph[u][v];
                    pq.add(new int[]{v, key[v]});
                }
            }
        }
        
        // 打印最小生成树
        System.out.println("\nPrim 最小生成树:");
        int minCost = 0;
        for (int i = 1; i < V; i++) {
            System.out.println(parent[i] + " - " + i + " : " + graph[i][parent[i]]);
            minCost += graph[i][parent[i]];
        }
        System.out.println("最小生成树总权重: " + minCost);
    }

    public static void main(String[] args) {
        // 创建示例图(邻接矩阵表示)
        int V = 5; // 节点数
        int[][] graph = {
            {0, 2, 0, 6, 0},  // 节点0
            {2, 0, 3, 8, 5},   // 节点1
            {0, 3, 0, 0, 7},   // 节点2
            {6, 8, 0, 0, 9},   // 节点3
            {0, 5, 7, 9, 0}    // 节点4
        };
        
        // 1. 执行Dijkstra算法
        dijkstra(graph, 0);
        
        // 2. 执行Floyd-Warshall算法
        floydWarshall(graph);
        
        // 3. 执行Kruskal算法
        List<Edge> edges = new ArrayList<>();
        // 添加所有边(无向图,每条边添加一次)
        edges.add(new Edge(0, 1, 2));
        edges.add(new Edge(0, 3, 6));
        edges.add(new Edge(1, 2, 3));
        edges.add(new Edge(1, 3, 8));
        edges.add(new Edge(1, 4, 5));
        edges.add(new Edge(2, 4, 7));
        edges.add(new Edge(3, 4, 9));
        kruskalMST(V, edges);
        
        // 4. 执行Prim算法
        primMST(graph);
    }
}

算法解析与输出结果

1. Dijkstra 算法(单源最短路径)

应用场景:路由选择、GPS导航、网络流量控制
时间复杂度:O(V²)(基础实现),O(E + V log V)(优先队列优化)
输出

Dijkstra 最短路径 (起点 0):
到节点 0 的最短距离: 0
到节点 1 的最短距离: 2
到节点 2 的最短距离: 5
到节点 3 的最短距离: 6
到节点 4 的最短距离: 7

2. Floyd-Warshall 算法(所有节点对最短路径)

应用场景:交通网络分析、社交网络关系计算、路由协议
时间复杂度:O(V³)
输出

Floyd-Warshall 所有节点对最短路径:
0 2 5 6 7 
2 0 3 8 5 
5 3 0 11 7 
6 8 11 0 9 
7 5 7 9 0 

3. Kruskal 算法(最小生成树)

应用场景:电网设计、通信网络建设、管道布局
时间复杂度:O(E log E)(主要来自排序)
输出

Kruskal 最小生成树:
0 - 1 : 2
1 - 2 : 3
1 - 4 : 5
0 - 3 : 6
最小生成树总权重: 16

4. Prim 算法(最小生成树)

应用场景:近似算法设计、网络设计、集群分析
时间复杂度:O(V²)(基础实现),O(E log V)(优先队列优化)
输出

Prim 最小生成树:
0 - 1 : 2
1 - 2 : 3
1 - 4 : 5
0 - 3 : 6
最小生成树总权重: 16

四大图算法对比

算法类型时间复杂度空间复杂度最佳应用场景
Dijkstra单源最短路径O(V²) / O(E + V log V)O(V)非负权图的最短路径
Floyd-Warshall全源最短路径O(V³)O(V²)稠密图的所有节点对距离
Kruskal最小生成树O(E log E)O(E + V)稀疏图的最小生成树
Prim最小生成树O(V²) / O(E log V)O(V)稠密图的最小生成树

关键实现技巧

  1. Dijkstra优化

    • 使用优先队列(最小堆)优化节点选择
    • 避免负权重边(会导致算法失效)
  2. Floyd-Warshall技巧

    • 三重循环顺序:k → i → j
    • 使用大数表示无穷大(避免整数溢出)
  3. Kruskal关键点

    • 使用并查集高效检测环路
    • 按边权重排序后再处理
  4. Prim优化

    • 优先队列维护候选边
    • 懒惰删除策略提高效率

实际应用建议

  1. 导航系统

    • 城市内路径规划:Dijkstra(单点对单点)
    • 全国路网分析:Floyd-Warshall(预计算所有点对)
  2. 网络设计

    • 局域网布线:Prim算法(节点密集)
    • 广域网连接:Kruskal算法(连接稀疏)
  3. 社交网络

    • 好友推荐:Dijkstra(寻找最近关系)
    • 影响力传播:Floyd-Warshall(计算关系距离)
  4. 优化选择

    • 图规模小(V<100):基础实现即可
    • 大规模图:优先使用优先队列优化的Dijkstra和Prim

提示:在实际项目中,优先使用成熟的图计算库如 JGraphT 或 Google Guava 中的图算法实现,它们经过了充分优化和测试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值