並查集解釋

並查集是算法中非常重要的概念也是常用的一個結構
網上看到我覺得最好的是https://blog.csdn.net/niushuai666/article/details/6662911,也就是我參考的博文
我用自己的語言刪減了一下進行記錄。

並查集的概念

並查集可以生動的用下圖表示。
可以看到是一顆顆的樹(圖),任意兩點可以相互訪問的是一類
一般來說,每個點只知道自己的父節點是什麼。最遠祖先(樹的根節點)的父節點是自身。最遠祖先相同的兩個節點屬於同一個集合。
兩個集合合併,只需要兩個點分別來自兩個集合,彼此可以訪問,那兩個集合就都聯通了。

在這裏插入圖片描述

算法實現

那麼算法需要實現的部分:
pre[]數組: 用於儲存這個節點的父節點。
int uninonsearch(int root) :查找根節點
void join(int root1, int root2) :將root1和root2所屬的兩個集合合併
因爲樹可能會變得特別深,不利於查找,所以需要一個路徑壓縮算法實現下圖:其實就是在尋找根節點過程中途徑的所有節點的父親設置爲根節點。
在這裏插入圖片描述

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int pre[1010]; //記錄每個點的父節點
int unionsearch(int root)
{
	int son, tmp;
	son = root;
	while(root != pre[root]) //尋找根節點
		root = pre[root];
	while(son != root) //已經找到了根節點,進行路徑壓縮
	{
		tmp = pre[son];
		pre[son] = root;
		son = tmp;
	}
	return root; //返回根節點
}
void join(int root1, int root2) //合併函數
{
	int x, y;
	x = unionsearch(root1);//找到根節點
	y = unionsearch(root2);//找到根節點
	if(x != y) 
		pre[x] = y; //合併,就是其中一個的父節點換爲另一個
}

int main()
{
	int num, road, total, i, start, end, root1, root2;
	while(scanf("%d%d", &num, &road) && num)
	{
		total = num - 1; //共num-1個門派
		for(i = 1; i <= num; ++i) //每條路都是掌門(自己自成一棵樹)
			pre[i] = i;
		while(road--)
		{
			scanf("%d%d", &start, &end); //他倆要結拜
			root1 = unionsearch(start);
			root2 = unionsearch(end);
			if(root1 != root2) //掌門不同?合併!
			{
				pre[root1] = root2;
				total--; //門派少一個,總的集合數減一
			}
		}
		printf("%d\n", total);//天下局勢:還剩幾個門派
	}
	return 0;
}

實例

題目鏈接
描述:
某省調查城鎮交通狀況,得到現有城鎮道路統計表,表中列出了每條道路直接連通的城鎮。省政府“暢通工程”的目標是使全省任何兩個城鎮間都可以實現交通(但不一定有直接的道路相連,只要互相間接通過道路可達即可)。問最少還需要建設多少條道路?

輸入:
測試輸入包含若干測試用例。每個測試用例的第1行給出兩個正整數,分別是城鎮數目N ( < 1000 )和道路數目M;隨後的M行對應M條道路,每行給出一對正整數,分別是該條道路直接連通的兩個城鎮的編號。爲簡單起見,城鎮從1到N編號。
注意:兩個城市之間可以有多條道路相通,也就是說
3 3
1 2
1 2
2 1
這種輸入也是合法的
當N爲0時,輸入結束,該用例不被處理。

解釋:
首先根據城市數量初始化有N個集合(每個城市自身爲一個)。根據有相互之間有路,來合併集合,最後得到還剩下多少集合(T個)。那就需要建立T-1條路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章