目錄
- 並查集模板
- 107. 尋找存在的路徑
並查集模板
原理:
並查集主要有兩個功能:
- 將兩個元素新增到一個集合中。
- 判斷兩個元素在不在同一個集合。
模板程式碼:
int n = 1005; // n根據題目中節點數量而定,一般比節點數量大一點就好
vector<int> father = vector<int> (n, 0); // C++裡的一種陣列結構
// 並查集初始化
void init() {
for (int i = 0; i < n; ++i) {
father[i] = i;
}
}
// 並查集裡尋根的過程
int find(int u) {
return u == father[u] ? u : father[u] = find(father[u]); // 路徑壓縮
}
// 判斷 u 和 v是否找到同一個根
bool isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
// 將v->u 這條邊加入並查集
void join(int u, int v) {
u = find(u); // 尋找u的根
v = find(v); // 尋找v的根
if (u == v) return ; // 如果發現根相同,則說明在一個集合,不用兩個節點相連直接返回
father[v] = u;
}
功能:
- 尋找根節點,函式:find(int u),也就是判斷這個節點的祖先節點是哪個。
- 將兩個節點接入到同一個集合,函式:join(int u, int v),將兩個節點連在同一個根節點上。
- 判斷兩個節點是否在同一個集合,函式:isSame(int u, int v),就是判斷兩個節點是不是同一個根節點。
按秩(rank)合併:
int n = 1005; // n根據題目中節點數量而定,一般比節點數量大一點就好
vector<int> father = vector<int> (n, 0); // C++裡的一種陣列結構
vector<int> rank = vector<int> (n, 1); // 初始每棵樹的高度都為1
// 並查集初始化
void init() {
for (int i = 0; i < n; ++i) {
father[i] = i;
rank[i] = 1; // 也可以不寫
}
}
// 並查集裡尋根的過程
int find(int u) {
return u == father[u] ? u : find(father[u]);// 注意這裡不做路徑壓縮
}
// 判斷 u 和 v是否找到同一個根
bool isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
// 將v->u 這條邊加入並查集
void join(int u, int v) {
u = find(u); // 尋找u的根
v = find(v); // 尋找v的根
if (rank[u] <= rank[v]) father[u] = v; // rank小的樹合入到rank大的樹
else father[v] = u;
if (rank[u] == rank[v] && u != v) rank[v]++; // 如果兩棵樹高度相同,則v的高度+1,因為上面 if (rank[u] <= rank[v]) father[u] = v; 注意是 <=
}
107. 尋找存在的路徑
題目連結:https://kamacoder.com/problempage.php?pid=1179
文章講解:https://programmercarl.com/kamacoder/0107.尋找存在的路徑.html
題目狀態:看題解
思路:
-
變數宣告:
n
:節點數量。father
:一個大小為 101 的向量,用於儲存每個節點的父節點。
-
並查集初始化:
init()
:將每個節點的父節點初始化為自身。
-
尋根操作:
find(int u)
:遞迴尋找節點 ( u ) 的根節點,並進行路徑壓縮以最佳化後續查詢。
-
判斷連通性:
isSame(int u, int v)
:檢查兩個節點 ( u ) 和 ( v ) 是否屬於同一個連通分量。
-
合併操作:
join(int u, int v)
:將節點 ( v ) 的根連線到節點 ( u ) 的根上,實現合併。
-
主函式:
- 讀取節點數 ( n ) 和邊數 ( m )。
- 呼叫
init()
初始化並查集。 - 讀取 ( m ) 條邊,每次呼叫
join(s, t)
將兩個節點連線。 - 讀取
source
和destination
,判斷它們是否連通。 - 輸出結果:如果連通,輸出 1;否則,輸出 0。
程式碼:
#include <iostream>
#include <vector>
using namespace std;
int n; // 節點數量
vector<int> father = vector<int> (101, 0); // 按照節點大小定義陣列大小
// 並查集初始化
void init() {
for (int i = 1; i <= n; i++) father[i] = i;
}
// 並查集裡尋根的過程
int find(int u) {
return u == father[u] ? u : father[u] = find(father[u]);
}
// 判斷 u 和 v是否找到同一個根
bool isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
// 將v->u 這條邊加入並查集
void join(int u, int v) {
u = find(u); // 尋找u的根
v = find(v); // 尋找v的根
if (u == v) return ; // 如果發現根相同,則說明在一個集合,不用兩個節點相連直接返回
father[v] = u;
}
int main() {
int m, s, t, source, destination;
cin >> n >> m;
init();
while (m--) {
cin >> s >> t;
join(s, t);
}
cin >> source >> destination;
if (isSame(source, destination)) cout << 1 << endl;
else cout << 0 << endl;
}