1. 程式人生 > >C++實現並查集

C++實現並查集

將N個不同的元素分成一組不相交的集合。 開始時,每個元素就是一個集合,然後按規律將兩個集合進行合併。

假如已知有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屬於一個另朋友圈,結果為兩個朋友圈。

最後請分析所寫程式碼的時間、空間複雜度。  

這個題用利用並查集實現會比較簡易和高效!

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;

class UnionFindSet
{
public:
	UnionFindSet(size_t size)
		:_array(new int[size])
		, _size(size)
	{
		memset(_array, -1, sizeof(int)*_size);
	}
	~UnionFindSet()
	{
		if (_array != NULL)
		{
			delete[] _array;
		}
	}
	void Merge(int root1, int root2)
	{
		while (_array[root2] >= 0)
		{
			root2 = _array[root2];

		}
		while (_array[root1] >= 0)
		{
			root1 = _array[root1];
		}
		_array[root1] += _array[root2];
		_array[root2] = root1;
	}
	int Find(int child)
	{
		while (_array[child] >= 0)
		{
			child = _array[child];
		}
		return child;
	}
	void print()
	{
		for (int i = 0; i < _size; ++i)
		{
			cout << _array[i] << " ";
		}
		cout << endl;
	}
	int friends(int n, int m, int r[][2])
	{
		UnionFindSet uf(n + 1);
		for (int i = 0; i < m; i++)
		{
			int first = r[i][0];
			int second = r[i][1];
			uf.Merge(first, second);
		}
		uf.print();
		int count = 0;
		for (int i = 1; i <= n; i++)
		{
			if (uf._array[i] < 0)
			{
				count++;
			}
		}
		return count;
	}
private:
	int*  _array;
	size_t _size;
};
void test()
{
	UnionFindSet us(10);
	us.Merge(0, 6);
	us.Merge(0, 7);
	us.Merge(0, 8);

	us.Merge(1, 4);
	us.Merge(1, 9);

	us.Merge(2, 3);
	us.Merge(2, 5);

	us.Merge(0, 4);
	us.print();
	cout << "4的朋友樹的父節點是" << us.Find(4) << endl;
	

	int n = 5;
	int m = 3;
	int r[][2]= { { 1, 2 }, { 2, 3 }, { 4, 5 } };
	int Frindcirle = us.friends(n, m, r);
	cout << Frindcirle << endl;
}

int main()
{
	test();
	system("pause");
	return 0;
}