首頁 >Java >java教程 >Java程式碼實作克魯斯卡爾演算法

Java程式碼實作克魯斯卡爾演算法

王林
王林轉載
2023-04-24 14:58:081055瀏覽

克魯斯卡爾演算法

克魯斯卡爾演算法是一種用於求解最小生成樹問題的貪婪演算法。最小生成樹是一個連通無向圖中生成樹中邊權值和最小的生成樹。克魯斯卡爾演算法以邊權值從小到大的順序依序選擇邊,當所選的邊不會形成環時,將其加入生成樹。具體實作過程如下:

  • 將所有邊依照邊權值從小到大排序。

  • 依序選擇邊,如果選擇的邊的兩個端點不在同一個連通分量中,則將這條邊加入到最小生成樹中,並將兩個端點合併為同一個連通分量。

  • 直到最小生成樹中包含了圖中的所有頂點為止。

演算法的優點在於只需要關注邊的權值,而與頂點的度數無關,因此在稠密圖中也能表現出較好的性能。同時,克魯斯卡爾演算法還具有較好的可擴展性,可以很方便地處理帶權圖中的最小生成森林問題。

執行流程

  • 將所有的邊依照權值從小到大排序;

  • 依序遍歷每條邊,如果這條邊連接的兩個節點不在同一個連通分量中,則將這條邊加入生成樹,並將這兩個節點合併為一個連通分量;

  • 重複步驟2 直到所有的節點都在同一個連通分量中,此時產生的樹即為最小生成樹。

在實作過程中,通常會使用並查集來維護連通性,以提高效率。

程式碼實作

import java.util.*;

public class KruskalAlgorithm {
    
    // 定义边的数据结构
    class Edge implements Comparable<Edge> {
        int src, dest, weight;
 
        public int compareTo(Edge edge) {
            return this.weight - edge.weight;
        }
    }
    
    // 并查集数据结构
    class Subset {
        int parent, rank;
    }
 
    int V, E; // V是顶点数,E是边数
    Edge edge[]; // 存储边的数组
 
    // 构造函数,初始化边和顶点数
    KruskalAlgorithm(int v, int e) {
        V = v;
        E = e;
        edge = new Edge[E];
        for (int i = 0; i < e; ++i)
            edge[i] = new Edge();
    }
 
    // 查找父节点
    int find(Subset subsets[], int i) {
        if (subsets[i].parent != i)
            subsets[i].parent = find(subsets, subsets[i].parent);
        return subsets[i].parent;
    }
 
    // 合并两个子集
    void union(Subset subsets[], int x, int y) {
        int xroot = find(subsets, x);
        int yroot = find(subsets, y);
 
        if (subsets[xroot].rank < subsets[yroot].rank)
            subsets[xroot].parent = yroot;
        else if (subsets[xroot].rank > subsets[yroot].rank)
            subsets[yroot].parent = xroot;
        else {
            subsets[yroot].parent = xroot;
            subsets[xroot].rank++;
        }
    }
 
    // 执行克鲁斯卡尔算法
    void kruskal() {
        Edge result[] = new Edge[V]; // 存储结果的数组
        int e = 0; // 表示result数组中的下标
 
        // 将边按照权重从小到大排序
        Arrays.sort(edge);
 
        // 创建V个子集
        Subset subsets[] = new Subset[V];
        for (int i = 0; i < V; ++i)
            subsets[i] = new Subset();
 
        // 初始化每个子集的父节点和秩
        for (int v = 0; v < V; ++v) {
            subsets[v].parent = v;
            subsets[v].rank = 0;
        }
 
        // 取E-1条边
        int i = 0;
        while (e < V - 1) {
            Edge next_edge = new Edge();
            next_edge = edge[i++];
 
            int x = find(subsets, next_edge.src);
            int y = find(subsets, next_edge.dest);
 
            // 如果两个节点不在同一个集合中,合并它们
            if (x != y) {
                result[e++] = next_edge;
                union(subsets, x, y);
            }
        }
 
        // 打印结果
        System.out.println("Following are the edges in the constructed MST");
        for (i = 0; i < e; ++i){
            System.out.println(result[i].src + " - " + result[i" - " + result[i].weight);
            return;
        }
        
        // 定义一个辅助函数,用于查找结点所在的集合 
        private int find(int parent[], int i) { 
            if (parent[i] == -1) 
                return i; 
            return find(parent, parent[i]); 
        }

        // 定义一个辅助函数,用于合并两个集合 
        private void union(int parent[], int x, int y) { 
            int xset = find(parent, x); 
            int yset = find(parent, y); 
            parent[xset] = yset; 
        } 
    }
}

函數使用Arrays類別的sort方法,依照邊的權重從小到大對邊進行排序。然後,函數依序遍歷排序後的邊,對於每條邊,使用find函數尋找其src和dest所在的集合的根節點。如果根節點不同,則表示這兩個集合不連通,可以合併,並將邊加入最小生成樹的結果陣列result。最後,函數遍歷最小生成樹的結果陣列result,並輸出每條邊的起點、終點和權重。

該實作中,使用了快速尋找集合的方法,即使用並查集來實現。每個結點都有一個parent數組,其中parent[i]表示結點i的父節點,如果parent[i] == -1,則說明結點i為根節點。在尋找結點所在的集合時,若目前結點的父節點為-1,則表示該結點為根節點,直接傳回;否則,遞迴尋找其父節點所在的集合。在合併兩個集合時,找到要合併的兩個集合的根節點,將其中一個根節點的父節點設為另一個根節點的索引,即將一個集合的根節點合併到另一個集合的根節點下。

這樣實作的克魯斯卡爾演算法時間複雜度為O(ElogE),其中E表示圖中的邊數,主要的時間開銷在於排序邊的過程。空間複雜度為O(V E),其中V表示圖中的頂點數,主要的空間開銷在於儲存邊和parent數組。

以上是Java程式碼實作克魯斯卡爾演算法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除