Kruskal 與 Union-Find

Union-Find 實作

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        self.rank = [0] * n
    
    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])  # 路徑壓縮
        return self.parent[x]
    
    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px == py: return False
        # 按秩合併
        if self.rank[px] < self.rank[py]:
            px, py = py, px
        self.parent[py] = px
        if self.rank[px] == self.rank[py]:
            self.rank[px] += 1
        return True

Kruskal MST

def kruskal(n, edges):
    """
    n: 節點數量
    edges: (u, v, weight)
    """
    edges.sort(key=lambda x: x[2])  # 按權重排序
    uf = UnionFind(n)
    mst = []
    total = 0
    
    for u, v, w in edges:
        if uf.union(u, v):
            mst.append((u, v, w))
            total += w
    
    return mst, total

Vibe Prompt

🔥 詠唱:「請用隨機產生的 50 個城市座標,實作 Kruskal MST 並用 Matplotlib 畫出生成樹。」


深入理解:Union-Find 的時間複雜度

Union-Find 搭配路徑壓縮 + 按秩合併後的時間複雜度近乎常數:

| 操作 | 未優化 | 路徑壓縮 | 路徑壓縮 + 按秩合併 | |------|:------:|:--------:|:------------------:| | find() | $O(n)$ | $O(\log n)$ | $O(\alpha(n))$ 攤銷 | | union() | $O(n)$ | $O(\log n)$ | $O(\alpha(n))$ 攤銷 | | Kruskal 總體 | $O(m \log m)$ | $O(m \log n)$ | $O(m \log n)$ |

$\alpha(n)$ 是反阿克曼函數,對於任何實際的 $n$,$\alpha(n) \leq 4$

Kruskal vs Prim 比較

| 特性 | Kruskal | Prim | |------|:-------:|:----:| | 演算法類型 | 邊為基礎 | 點為基礎 | | 適合 | 稀疏圖 ($m \approx n$) | 密集圖 ($m \approx n^2$) | | 資料結構 | Union-Find | 最小堆積 (Priority Queue) | | 時間複雜度 | $O(m \log m)$ | $O(m \log n)$ | | 是否需要連通 | 不需要 | 需要連通圖 |


關鍵要點

  • ✅ Kruskal 演算法:按權重排序邊,用 Union-Find 避免環
  • ✅ Union-Find 路徑壓縮 + 按秩合併 = 近乎 O(1) 的操作
  • ✅ Kruskal 適合稀疏圖,Prim 適合密集圖
  • ✅ MST 的最小性證明:割性質 (Cut Property)
  • ✅ MST 在網路設計、叢集分析中有廣泛應用

Union-Find 的實戰應用:不只是 Kruskal

Union-Find(Disjoint Set)是你學過最實用但最被低估的資料結構。它不只用在 Kruskal 演算法——任何需要「動態分組」或「偵測連通性」的場景,Union-Find 都是最佳選擇。

真實世界的 Union-Find 應用

| 應用 | 問題 | Union-Find 的角色 | |:----|:----|:----------------| | 社群網路好友推薦 | A 是 B 的好友,B 是 C 的好友 → A、B、C 同一群組 | 維護朋友圈的連通分量 | | 圖片像素連通域 | 找出圖片中所有相連的相同顏色區域 | 合併相鄰的同色像素 | | 迷宮生成與求解 | 隨機打通牆壁直到起點終點連通 | 偵測兩點是否已經連通,避免產生環 | | 電路板短路檢測 | 兩個接腳是否屬於同一網路 | 合併相連的節點,查詢連通性 | | 資料庫合併 | 兩筆紀錄是否屬於同一個人 | 合併相同人的 ID |

為什麼 Union-Find 這麼快?

路徑壓縮(Path Compression)+ 按秩合併(Union by Rank)讓 Union-Find 的攤銷時間複雜度變成 O(α(n)),而反阿克曼函數 α(n) 對於任何實際的 n 都小於 5。

換句話說,Union-Find 的操作可以視為 O(1)——這是理論上你能得到的最快速度。

下一章預告:Prim 演算法

Kruskal 是「以邊為中心」的 MST 演算法,而 Prim 是「以點為中心」——從起點出發,每一步加入最近的節點。下一章你將學到 Prim 搭配 Priority Queue 的實作,以及 Kruskal 與 Prim 各自的適用場景。

會員專屬免費教學

本章節為註冊會員專屬的免費開放內容!請先登入或註冊會員,即可立即解鎖閱讀。

立即登入 / 註冊