一、介紹
- 背景
前面將到並查集基於size的優化,其實仔細想想,還是有可以優化的地方;size[i]是指以i為根節點樹的節點數;是將節點數量多的樹的根節點向節點數好的樹的根節點連線,在一般情況下是得到了優化,但是這裡就存在問題了,當出現:節點數多的樹它的高度非常高的時候,size的優化方式就不太高效了。 - rank
rank[i]:是用來記錄以i為根節點的樹的高度(樹的層數),其本質是陣列。
二、邏輯
並查集本質是樹,當樹的高度(層數)越高在對數的操作其複雜度會越高,rank的目的就是降低在並(union)過程中並查集的高度;在並(union)過程中使用rank來記錄合併的兩棵樹的高度,將rank值小的樹的根節點指向rank值大的根節點。如下圖:
連線2,4( union(4,2) )
方法一:
方法二:
很明顯方法二比方法一更優
方法二:正是基於rank的優化
具體邏輯如下:
rank[7] = 2
rank[8] = 3
此時只需要將rank[7]樹的根節點指向rank[8]樹的節點
合併後,如下:
此時整個並查集rank[8] = 3,高度不變
三、程式碼實現
#include<cassert>
using namespace std;
namespace UF4 {
class UnionFind4 {
private:
// 我們的第二版Union-Find, 使用一個陣列構建一棵指向父節點的樹
// parent[i]表示第i個元素所指向的父節點
int *parent;
int *rank;
int count; //資料個數
public:
UnionFind4(int count) {
parent = new int[count];
rank = new int[count];
this->count = count;
//初始化
for (int i = 0; i < count; i++) {
parent[i] = i;
}
}
//解構函式
~UnionFind4() {
delete parent;
delete rank;
}
// 查詢過程, 查詢元素p所對應的集合編號
// O(h)複雜度, h為樹的高度
int find(int p) {
assert(p >= 0 && p <= count);
// 不斷去查詢自己的父親節點, 直到到達根節點
// 根節點的特點: parent[p] == p
while (p != parent[p])
p = parent[p];
return p;
}
// 檢視元素p和元素q是否所屬一個集合
// O(h)複雜度, h為樹的高度
bool isConnected(int p, int q) {
return find(p) == find(q);
}
rank核心部分:
// 合併元素p和元素q所屬的集合
// O(h)複雜度, h為樹的高度
void unionElments(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot)
return;
if (rank[pRoot] > rank[qRoot]) {
parent[pRoot] = qRoot;
}
else if (rank[pRoot] < rank[qRoot]) {
parent[qRoot] = pRoot;
}
else {//rank[pRoot] == rank[qRoot]
parent[qRoot] = pRoot;
rank[qRoot] = +1;
}
}
};
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結