並查集是一種維護集合的數據結構,「並」,「查」,「集」 三個字分別取自 Union(合併),Find(查找),Set(集合)。並查集是若干個不相交集合,可以在 \(O(1)\) 實現兩個集合的合併,判斷兩個元素是否屬於同一集合應用,如其求無向圖的連通份量個數、實現kruskal算法求最小生成樹。php
並查集的實現方法就是一個數組:html
int pre[N];
其中 pre[i]
表示元素 i
結點的父節點,pre[i]
和 i
結點屬於同一集合。例如:pre[1] = 2
就代表元素1的父節點是元素2,元素1和元素2屬於同一集合。若是 pre[i]==i
則說明元素 i
是該集合的根結點,對於同一個集合來講,只存在一個根結點。node
初始化一個pre數組。用於記錄了每一個節點屬於哪一個集合;初始時數組內的值與數組的下角標一致。即每一個數字都本身一個集合。ios
void initialize(int n) { for (int i = 1; i <= n; i++) { pre[i] = i; } }
查找的目的是找到集合的 根結點 Big Boss,而不是前驅,由於前驅不必定是根結點,好比圖中的5,咱們要找到的是3,而不是4。算法
看這幅圖,咱們能夠知道,當出現 pre[x]==x 時,證實找到了 Big Boss。數組
這裏出現了一個問題,5和4的「頂頭上司」都是3,可是5卻要多走了一步,若是這個層級更多,那勢必會下降算法的效率。數據結構
因而就有了在查找過程當中的路徑壓縮spa
//查找 int Find(int x) { if (pre[x] == x)return x; return pre[x] = Find(pre[x]); ////你爸爸的爸爸就是你的爸爸 }
初始三個節點,各自爲一個集合3d
以下圖,合併3,4操做,此時3和4合併爲一個集合。3掛在4上,仍是4掛在3上都是同樣的,它們都表示3和4是同一個集合,只是集合的Big Boss不同。code
咱們繼續進行合併4,5操做,經過路徑壓縮咱們得到了右邊的形式,此時三個數已經爲同一個集合了。
//合併 void Union(int x, int y) { int fx = Find(x), fy = Find(y); //若是x,y已是同一集合了,返回 if (fx == fy) return; //這裏經過深度來肯定fx掛fy上,仍是fy掛在fx上,實際意義不大,就簡單點寫了。 pre[fy] = fx; }
完整代碼,這鬼東西好用就好用在很短,真的太好寫了
int pre[5001]; void initialize(int n) { for (int i = 1; i <= n; i++) { pre[i] = i; } } int Find(int x) { if (x == pre[x])return pre[x]; else return pre[x] = Find(pre[x]); } void Union(int x, int y) { int fx = Find(x), fy = Find(y); if (fx == fy) return; pre[fy] = fx; }
HDU 1232 :http://acm.hdu.edu.cn/showproblem.php?pid=1232
基本思想是:N個節點,最少須要 N-1 根線連起來。 把輸入的城鎮所有進行Union,若是這兩個城鎮不連通,N--,最後輸出 N - 1就是答案。
#include<stdio.h> int Pre[1001]; //查找 int Find(int x) { if (Pre[x] == x)return x; return Pre[x] = Find(Pre[x]); } //合併 void Union(int x, int y, int &n) { int fx = Find(x), fy = Find(y); if (fx == fy) return; Pre[fx] = fy; n--; } int main() { int n, m; while (~scanf("%d", &n)) { if (n == 0) break; for (int i = 0; i <= n; i++) Pre[i] = i; scanf("%d", &m); while (m--) { int c1, c2; scanf("%d%d", &c1, &c2); Union(c1, c2, n); } printf("%d\n", n - 1); } return 0; }
題目連接:https://www.luogu.org/problem/P3366
題解: http://www.javashuo.com/article/p-djiasmtz-cy.html
求連通塊數量,沒找着簡單的模板題,代碼比較容易理解。
#include <iostream> using namespace std; int pre[maxn]; int Find(int x) { if (pre[x] == x)return x; return pre[x] = Find(pre[x]); } void Union(int x, int y) { int fx = Find(x), fy = Find(y); if (fx == fy) return; pre[fx] = fy; } int main(){ int m, n; cin >> n; for (int i = 0; i < n; i++) pre[i] = i; cin >> m; while (m--) { int c1, c2; cin >> c1 >> c2; Union(c1, c2);//兩點之間有路的屬於同一個連通塊,合併起來 } int cnt = 0; for (int i = 0; i < n; i++) { if (pre[i] == i) cnt++; //有多少個根結點,就有多少個連通塊 } cout << cnt << endl; return 0; }
寫了玩的
#include <iostream> using namespace std; int m, n; int cnt = 0; int maze[500][500]; bool book[500][500]; int dir[4][2] = { {0,-1},{0,1},{-1,0},{1,0} }; void dfs(int x, int y) { for (int i = 0; i < 4; i++) { int tox = x + dir[i][0], toy = y + dir[i][1]; if (!book[tox][toy] && maze[tox][toy] == 1 && (tox >= 0 && tox < m && toy >= 0 && toy < n)) { book[tox][toy] = true; dfs(tox, toy); } } return; } int main() { cin >> m >> n; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { cin >> maze[i][j]; } } for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (maze[i][j] && !book[i][j]) { dfs(i, j); cnt++; } } } cout << cnt; return 0; }
#include <iostream> #include <stdio.h> #include <queue> using namespace std; struct node { int x; int y; node(int x, int y) :x(x), y(y) {}; }; queue<node> q; int maze[105][105]; bool book[105][105]; int m, n; int dir[4][2] = { {0,-1},{0,1},{-1,0},{1,0} }; void bfs(int x1, int y1) { node nn; nn.x = x1; nn.y = y1; while (!q.empty()) q.pop(); q.push(nn); book[x1][y1] = true; while (q.empty() == false) { node now = q.front(); q.pop(); for (int i = 0; i < 4; i++) { int tox = now.x + dir[i][0]; int toy = now.y + dir[i][1]; if (!book[tox][toy] && maze[tox][toy] == 1 && (tox >= 0 && tox < m && toy >= 0 && toy < n)) { book[tox][toy] = true; q.push(node(tox, toy)); } } } } int main(int argc, char** argv) { cin >> m >> n; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { cin >> maze[i][j]; } } int ans = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (maze[i][j] && !book[i][j] ) { bfs(i, j); ans++; } } } cout << ans << endl; return 0; }