一、概念及其介紹
並查集是一種樹型結構,用於處理一些不相交集合的合併及查詢問題。
並查集的思想是用一個陣列表示了整片森林(parent),樹的根節點唯一標識了一個集合,我們只要找到了某個元素的的樹根,就能確定它在哪個集合裡。
對於並查集主要支援兩個操作:
- 並{ union(p,q) }
- 查詢{ find(p) }
來回答一個問題:連線{ inConnected(p, q) }
二、並查集的基本資料表示
難點分析:橫向上的數值其實是對應橫線下資料的在陣列中的索引值,也就是說橫線下是一個真正的陣列,而橫線上則是陣列對應的索引,在這裡我們是用索引當作元素,用陣列資料值的異同來表示元素是否連線。
橫線上:用陣列索引表示元素
橫線下:表示連線情況(值為0的表示在一個集合即連線)
所以0-4在同一個集合,5-9在同一個集合:
三、程式碼實現
下面我們來介紹並查集的主要操作:
我們先實現一個並查集:
#include <iostream>
#include <cassert>
using namespace std;
// 我們的第一版Union-Find
namespace UF1 {
class UnionFind {
private:
int *id; // 第一版Union-Find本質就是一個陣列
int count; // 資料個數
public:
// 建構函式
UnionFind(int n) {
count = n;
id = new int[n];
// 初始化, 每一個id[i]指向自己, 沒有合併的元素,每一個數都是一個集合
for (int i = 0; i < n; i++)
id[i] = i;
}
// 解構函式
~UnionFind() {
delete[] id;
}
find的實現:(查詢元素所在的集合編號,直接返回陣列值,O(1) 的時間複雜度。)
// 查詢過程, 查詢元素p所對應的集合編號 int find(int p) { assert(p >= 0 && p < count); return id[p]; }
isConnected的實現:
// 檢視元素p和元素q是否所屬一個集合 // O(1)複雜度 bool isConnected(int p, int q) { return find(p) == find(q); }
union的實現:(合併元素 p 和元素 q 所屬的集合, 合併過程需要遍歷一遍所有元素, 再將兩個元素的所屬集合編號合併,這個過程是 O(n) 複雜度。)
// 合併元素p和元素q所屬的集合 // O(n) 複雜度 void unionElements(int p, int q) { //union在c++中是一個關鍵字,所以這裡用 unionElements int pID = find(p); int qID = find(q); if (pID == qID) return; // 合併過程需要遍歷一遍所有元素, 將兩個元素的所屬集合編號合併 for (int i = 0; i < count; i++) if (id[i] == pID) id[i] = qID; }
本作品採用《CC 協議》,轉載必須註明作者和本文連結