并查集

     并查集是一种树形的数据结构,用来处理不相交的集合的合并和查询。原理是:先开辟一个和数据大小相等的数组a,数组数据初始化为-1,当数据x1,x2同属一个集合,将数组a[x2]加到a[x1]上,并将x1赋给a[x2],这样,如果检查的时候数组的值小于0,则为根节点,并且这个值的绝对值为这个集合元素的个数。如果检查的时候数组的值大于0,则不是根节点,数组的值为上一节点的数组的值。下面用一个例子说明。

小米的校招题:
朋友圈(25分)
假如已知有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属于另一个朋友圈,结果为2个朋友圈。
最后请分析所写代码的时间、空间复杂度。评分会参考代码的正确性和效率。
C/C++:
int friends(int n , int m , int* r[]);

//代码实现如下:

#pragma once
class unionFindset
{
public:

//开辟一段和数组个数相等大小的数组空间
unionFindset(size_t size)
:_a(new int[size])
, _size(size)
{

//将数组中数据元素都初始化为-1
for (int i = 0; i < size; ++i)
{
_a[i] = -1;
}
}

//合并
void combine(int root1, int root2)
{
if (_a[root1] < 0 && _a[root2] < 0)
{
_a[root1] += _a[root2];
_a[root2] = root1;
}
}
//寻找根节点,即数组中元素小于0的元素
int FindRoot(int child)
{
while (_a[child] >= 0)
child = _a[child];
return child;
}

int count()
{
int count = 0;
for (int i = 0; i < _size; ++i)
{
if (_a[i]<=0)
count++;
}
return count;
}
protected:
int *_a;
int _size;
};

int Friends(int n, int m, int r[][2])
{
assert(r);
unionFindset us(n);
for (int i = 0; i < m; ++i)
{
int first = r[i][0];
int second = r[i][1];
int root1 = us.FindRoot(first);
int root2 = us.FindRoot(second);
if (root1 != root2)
{
us.combine(root1, root2);
}
}

//注意:这里count要减1,因为开辟的数组下标是从0开始的,而实际存取数据是从1开始的,因此要减1
return us.count() -1;
}

原文地址:https://www.cnblogs.com/qingjiaowoxiaoxioashou/p/5517405.html