Java實(shí)現(xiàn)克魯斯卡爾算法的示例代碼
克魯斯卡爾算法
克魯斯卡爾算法是一種用于求解最小生成樹問題的貪心算法。最小生成樹是一個(gè)連通無向圖中生成樹中邊權(quán)值和最小的生成樹??唆斔箍査惴ò催厵?quán)值從小到大的順序依次選擇邊,當(dāng)所選的邊不會(huì)形成環(huán)時(shí),將其加入到生成樹中。具體實(shí)現(xiàn)過程如下:
- 將所有邊按照邊權(quán)值從小到大排序。
- 依次選擇邊,如果選擇的邊的兩個(gè)端點(diǎn)不在同一個(gè)連通分量中,則將這條邊加入到最小生成樹中,并將兩個(gè)端點(diǎn)合并為同一個(gè)連通分量。
- 直到最小生成樹中包含了圖中的所有頂點(diǎn)為止。
算法的優(yōu)點(diǎn)在于只需要關(guān)注邊的權(quán)值,而與頂點(diǎn)的度數(shù)無關(guān),因此在稠密圖中也能表現(xiàn)出較好的性能。同時(shí),克魯斯卡爾算法還具有較好的可擴(kuò)展性,可以很方便地處理帶權(quán)圖中的最小生成森林問題。
執(zhí)行流程
- 將所有的邊按照權(quán)值從小到大排序;
- 依次遍歷每條邊,如果這條邊連接的兩個(gè)節(jié)點(diǎn)不在同一個(gè)連通分量中,則將這條邊加入生成樹,并將這兩個(gè)節(jié)點(diǎn)合并為一個(gè)連通分量;
- 重復(fù)步驟 2 直到所有的節(jié)點(diǎn)都在同一個(gè)連通分量中,此時(shí)生成的樹即為最小生成樹。
在實(shí)現(xiàn)過程中,通常使用并查集來維護(hù)連通性,以提高效率。
代碼實(shí)現(xiàn)
import java.util.*;
public class KruskalAlgorithm {
// 定義邊的數(shù)據(jù)結(jié)構(gòu)
class Edge implements Comparable<Edge> {
int src, dest, weight;
public int compareTo(Edge edge) {
return this.weight - edge.weight;
}
}
// 并查集數(shù)據(jù)結(jié)構(gòu)
class Subset {
int parent, rank;
}
int V, E; // V是頂點(diǎn)數(shù),E是邊數(shù)
Edge edge[]; // 存儲(chǔ)邊的數(shù)組
// 構(gòu)造函數(shù),初始化邊和頂點(diǎn)數(shù)
KruskalAlgorithm(int v, int e) {
V = v;
E = e;
edge = new Edge[E];
for (int i = 0; i < e; ++i)
edge[i] = new Edge();
}
// 查找父節(jié)點(diǎn)
int find(Subset subsets[], int i) {
if (subsets[i].parent != i)
subsets[i].parent = find(subsets, subsets[i].parent);
return subsets[i].parent;
}
// 合并兩個(gè)子集
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++;
}
}
// 執(zhí)行克魯斯卡爾算法
void kruskal() {
Edge result[] = new Edge[V]; // 存儲(chǔ)結(jié)果的數(shù)組
int e = 0; // 表示result數(shù)組中的下標(biāo)
// 將邊按照權(quán)重從小到大排序
Arrays.sort(edge);
// 創(chuàng)建V個(gè)子集
Subset subsets[] = new Subset[V];
for (int i = 0; i < V; ++i)
subsets[i] = new Subset();
// 初始化每個(gè)子集的父節(jié)點(diǎn)和秩
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);
// 如果兩個(gè)節(jié)點(diǎn)不在同一個(gè)集合中,合并它們
if (x != y) {
result[e++] = next_edge;
union(subsets, x, y);
}
}
// 打印結(jié)果
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;
}
// 定義一個(gè)輔助函數(shù),用于查找結(jié)點(diǎn)所在的集合
private int find(int parent[], int i) {
if (parent[i] == -1)
return i;
return find(parent, parent[i]);
}
// 定義一個(gè)輔助函數(shù),用于合并兩個(gè)集合
private void union(int parent[], int x, int y) {
int xset = find(parent, x);
int yset = find(parent, y);
parent[xset] = yset;
}
}
}
函數(shù)使用Arrays類的sort方法,按照邊的權(quán)重從小到大對(duì)邊進(jìn)行排序。然后,函數(shù)依次遍歷排序后的邊,對(duì)于每條邊,使用find函數(shù)查找其src和dest所在的集合的根節(jié)點(diǎn)。如果根節(jié)點(diǎn)不同,則說明這兩個(gè)集合不連通,可以合并,并將邊加入最小生成樹的結(jié)果數(shù)組result中。最后,函數(shù)遍歷最小生成樹的結(jié)果數(shù)組result,并輸出每條邊的起點(diǎn)、終點(diǎn)和權(quán)重。
該實(shí)現(xiàn)中,使用了快速查找集合的方法,即使用并查集來實(shí)現(xiàn)。每個(gè)結(jié)點(diǎn)都有一個(gè)parent數(shù)組,其中parent[i]表示結(jié)點(diǎn)i的父節(jié)點(diǎn),如果parent[i] == -1,則說明結(jié)點(diǎn)i為根節(jié)點(diǎn)。在查找結(jié)點(diǎn)所在的集合時(shí),如果當(dāng)前結(jié)點(diǎn)的父節(jié)點(diǎn)為-1,則說明該結(jié)點(diǎn)為根節(jié)點(diǎn),直接返回;否則,遞歸查找其父節(jié)點(diǎn)所在的集合。在合并兩個(gè)集合時(shí),找到要合并的兩個(gè)集合的根節(jié)點(diǎn),將其中一個(gè)根節(jié)點(diǎn)的父節(jié)點(diǎn)設(shè)為另一個(gè)根節(jié)點(diǎn)的索引,即將一個(gè)集合的根節(jié)點(diǎn)合并到另一個(gè)集合的根節(jié)點(diǎn)下。
這樣實(shí)現(xiàn)的克魯斯卡爾算法時(shí)間復(fù)雜度為O(ElogE),其中E表示圖中的邊數(shù),主要的時(shí)間開銷在于排序邊的過程??臻g復(fù)雜度為O(V+E),其中V表示圖中的頂點(diǎn)數(shù),主要的空間開銷在于存儲(chǔ)邊和parent數(shù)組。
到此這篇關(guān)于Java實(shí)現(xiàn)克魯斯卡爾算法的示例代碼的文章就介紹到這了,更多相關(guān)Java克魯斯卡爾算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java如何實(shí)現(xiàn)簡單后臺(tái)訪問并獲取IP
這篇文章主要介紹了Java如何實(shí)現(xiàn)簡單后臺(tái)訪問并獲取IP,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
Java 網(wǎng)絡(luò)爬蟲基礎(chǔ)知識(shí)入門解析
這篇文章主要介紹了Java 網(wǎng)絡(luò)爬蟲基礎(chǔ)知識(shí)入門解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
Java中用Mybatis插入mysql報(bào)主鍵重復(fù)的解決方案
這篇文章主要介紹了Java中用Mybatis插入mysql報(bào)主鍵重復(fù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2023-02-02
如何將Java與C#時(shí)間進(jìn)行互相轉(zhuǎn)換
這篇文章主要介紹了Java與C#時(shí)間互轉(zhuǎn)的方法以及JAVA日期、C#日期計(jì)算說明,需要的朋友可以參考下2022-11-11
spring?aop?pointcut?添加多個(gè)execution方式
這篇文章主要介紹了spring?aop?pointcut?添加多個(gè)execution方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

