並查集的概念與演算法實現

聽聞__發表於2018-04-25

1、定義

        並查集支援查詢一個元素所屬的集合以及合併兩個元素各自所屬的集合等運算。當給出兩個元素的一個無序對(a,b),需要快速“合併”a和b所在的集合時,需要反覆“查詢”元素的集合,“並”、“查”和“集”三字由此而來。

2、應用

並查集主要用於處理一些不相交集合的合併及查詢問題,它可以高效的解決一些等價類問題。

3、實現

(1)並查集的樹結構型別定義

typedef int ElemType;

//union-find set
typedef struct UFSTNode
{
	ElemType data;
	int rank;
	int parent;
}UFSTNode;

(2)並查集的建立

void MAKE_SET(UFSTNode ufs[], ElemType _data[], int n)
{
	for (int i = 0; i < n; i++)
	{
		ufs[i].data = _data[i];
		ufs[i].parent = i;
		ufs[i].rank = 1;
	}
}

(3)並查集的查詢

int FIND_SET(UFSTNode ufs[], int x)
{
	while (x != ufs[x].parent)
		x = ufs[x].parent;
	return x;
}

(4)並查集的合併

void UNION(UFSTNode ufs[], int x, int y)
{
	int s_x = FIND_SET(ufs, x);
	int s_y = FIND_SET(ufs, y);
	if (s_x > s_y)
		ufs[s_y].parent = s_x;
	else
	{
		ufs[s_x].parent = s_y;
		if (ufs[s_x].rank == ufs[s_y].rank)
			ufs[s_y].rank++;
	}
}

4、測試

        問題: 親威關係問題。現給出一些親戚關係的資訊,如Marry和Tom是親戚,Tom希

Ben是親戚等。從這些資訊中,推出Marry 和Ben是否是親戚。

        輸入: 第一部分以N,M開始。N 為問題涉及的人的個數(1<=N<=20000),這些人的家
號為1,2,3,…,N。下面有M行(1<=M<=1000000),每行有兩個數a,b,表示已知 ai 和 bi 是親威。
        第二部分以Q 開始。以下Q行有Q個詢問(1<=Q<=1000000),每行為ci,di,表示詢向

ci和di是否為親戚。

        輸出: 對於每個詢問ci,di,若 ci 和 di 為親戚,則輸出"Yes”,否則輸出“No"。

        輸入樣例:

        10 7

        2 4 

        5 7

        1 3 

        8 9

        1 2

        5 6

        2 3

        3

        3 4

        7 10

        8 9

#include <iostream>
using namespace std;

typedef int ElemType;

//union-find set
typedef struct UFSTNode
{
	ElemType data;
	int rank;
	int parent;
}UFSTNode;

void MAKE_SET(UFSTNode ufs[], ElemType _data[], int n)
{
	for (int i = 0; i < n; i++)
	{
		ufs[i].data = _data[i];
		ufs[i].parent = i;
		ufs[i].rank = 1;
	}
}

int FIND_SET(UFSTNode ufs[], int x)
{
	while (x != ufs[x].parent)
		x = ufs[x].parent;
	return x;
}

void UNION(UFSTNode ufs[], int x, int y)
{
	int s_x = FIND_SET(ufs, x);
	int s_y = FIND_SET(ufs, y);
	if (s_x > s_y)
		ufs[s_y].parent = s_x;
	else
	{
		ufs[s_x].parent = s_y;
		if (ufs[s_x].rank == ufs[s_y].rank)
			ufs[s_y].rank++;
	}
}

int main()
{
	//input
	const int n = 11, m = 7, q = 3;
	const ElemType data[n] = {};
	const int relate_set[m][2] = { {2, 4}, {5, 7}, {1, 3}, {8, 9}, {1, 2}, {5, 6}, {2, 3} };
	const int query_set[q][2] = { {3, 4}, {7, 10}, {8, 9} };
	//execute
	UFSTNode* ufs = new UFSTNode[n];
	MAKE_SET(ufs, const_cast<ElemType*>(data), n);
	for (int i = 0; i < m; i++)
	{
		UNION(ufs, relate_set[i][0], relate_set[i][1]);
	}
	//output
	for (int i = 0; i < q; i++)
	{
		if (FIND_SET(ufs, query_set[i][0]) == FIND_SET(ufs, query_set[i][1]))
			cout << "Yes" << endl;
		else
			cout << "No" << endl;
	}
	//clear
	delete[] ufs;
	system("pause");
	return 0;
}

參考文獻

[1] 李春葆.資料結構教程.清華大學出版社,2013.

相關文章