資料結構 — 並查集的原理與應用

Dawn_sf發表於2017-11-29

並查集的原理與應用





並查集是一種樹型的資料機構,常用於處理一些不相交集合的合併及查詢問題.並查集是一種簡單的資料結構主要涉及兩個基本操作:

1.合併兩個不相交的集合.

2.判斷兩個元素是否屬於同一個集合.


並查集的實現原理:


首先並查集會開闢出一個陣列,假設陣列有10個人,那麼就會開闢10個大小的陣列,然後每一個下標代表一個人.陣列每一個節點的值

都為-1.表示每一個節點都是相對應的一個集合.大家都不相交.這個時候開始新增關係.如下圖:


所以我們發現現在確定一共有幾個集合,還有查詢兩個資料在不在一個集合當中就很容易了. 首先確定幾個集合你只需要便利一遍,

如果那個下標的值小於0,那麼就是一個集合.然後判斷兩個資料是不是在同一個集合裡面也很簡單. 分別找兩個資料的根.如果根

相同那麼他兩個就在一個集合裡,反之就不在.好了現在思想確定了,那麼我們過來實現程式碼.

class UnionFind
{
public:
	UnionFind(int n)
	{
		_UnionTable.resize(n+1); //因為一般0號下標沒有用,所以多開出來一個空間.

		for (int i = 0; i < n+1; ++i)
		{
			_UnionTable[i] = -1;
		}
	}
	
	void AddUnion(int i,int j)
	{
		if (_UnionTable[i] < 0 && _UnionTable[j] < 0)
		{
			_UnionTable[i] += _UnionTable[j];
			_UnionTable[j] = i;
		}
		else
		{
			int k = FindRoot(i);
			int l = FindRoot(j);

			_UnionTable[k] += _UnionTable[l];
			_UnionTable[l] = k;
		}
	}

	int FindRoot(int i)
	{
		assert(i >= 0);

		int k = i;
		while (_UnionTable[k] >= 0)
		{
			k = _UnionTable[k];
		}

		return k;
	}

	int size()
	{
		int count = 0;

		for (int i = 0; i < _UnionTable.size(); ++i)
		{
			if (_UnionTable[i] < 0)
			{
				++count;
			}
		}

		return count - 1;
	}

private:
	vector<int>  _UnionTable;
};

這個程式碼就是實現一個最簡易的並查集,可以用它解決一點實際問題,比如當年小米的一道面試題! 40分啊! 好看題:

假設一組有N個人和M對好友關係(存於陣列R).如果兩個人是直接或者間接好友(好友的好友就是間接好友),則認為他們屬於同一個朋

友圈,請寫出程求出這個N個人裡面一共有多少個朋友圈.

例如:N =5,M =3,R = {{1,2},{2,3},{4,5}} 表示有5個人,1和2是還有,2和3是好友,4和5是好友,則1,2,3屬於一個朋友圈,4,5

屬於一個朋友圈. 則一共擁有兩個朋友圈.   函式原型 : int friends(int m,int n,int* r[]);


首先拿到這個題我在不知道並查集這個資料結構之前是真的一頭霧水,用其他資料結構真的很難解決.但是使用並查集輕而易舉就解決

掉了,不相信?  我下面利用並查集演示解決該題:

class UnionFind
{
public:
	UnionFind(int n)
	{
		_UnionTable.resize(n+1); //因為一般0號下標沒有用,所以多開出來一個空間.

		for (int i = 0; i < n+1; ++i)
		{
			_UnionTable[i] = -1;
		}
	}
	
	void AddUnion(int i,int j)
	{
		if (_UnionTable[i] < 0 && _UnionTable[j] < 0)
		{
			_UnionTable[i] += _UnionTable[j];
			_UnionTable[j] = i;
		}
		else
		{
			int k = FindRoot(i);
			int l = FindRoot(j);

			_UnionTable[k] += _UnionTable[l];
			_UnionTable[l] = k;
		}
	}

	int FindRoot(int i)
	{
		assert(i >= 0);

		int k = i;
		while (_UnionTable[k] >= 0)
		{
			k = _UnionTable[k];
		}

		return k;
	}

	int size()
	{
		int count = 0;

		for (int i = 0; i < _UnionTable.size(); ++i)
		{
			if (_UnionTable[i] < 0)
			{
				++count;
			}
		}

		return count - 1;
	}

private:
	vector<int>  _UnionTable;
};

int Friends(int m,int n,int a[][2])
{
	UnionFind T(m);

	for (int i = 0; i < n; i++)
	{
		T.AddUnion(a[i][0],a[i][1]);
	}

	return T.size();
}

void Test()
{
	int n = 5;
	int a[][2] = { { 1, 2 }, { 2, 3 }, { 4, 5 } };

	cout << "一共有"<<Friends(5, 3, a)<<"個朋友圈"<<endl;

}

執行結果為:



相關文章