(一)并查集的引入
以小米的这道题为例
并查集定义:并查集实际上是右一个数组实现的,这个数组比较特殊,最开始将数组的每一个数据看成一个单独的集合,用-1表示。然后根据题目要求1和2可以合并,将第2个数据合并到1上时,array[1]+=array[2],array[2]=1(数组的array[1]保存其与array[2]累加的值,这个值为负,array[2]保存他合并到的那个结点)
通过并查集保存过后的数据如上图所示,那么最后只需要计数数组中负数的个数就是朋友圈的个数。
(二)实现代码
由于面试的时候有两种选择,使用类或直接用函数实现,我这里给出两种方式的代码:
1.使用C++类:
测试时包上头文件即可
class UnionFindSet
{
private:
int *_array;
size_t _size;
public:
UnionFindSet()
:_array(NULL)
, _size(0)
{}
UnionFindSet(int size)
:_array(new int[size])
, _size(size)
{
memset(_array, -1, sizeof(size_t)*size);
}
int FindRoot(int child)
{
if (_array[child] > 0)
{
return _array[child];
}
else
{
return child;
}
}
void Combine(int child1, int child2)
{
int root1 = FindRoot(child1);
int root2 = FindRoot(child2);
_array[root1] += _array[root2];
_array[root2] = root1;
}
int FriendCount()
{
int count=0;
for (int i = 0; i <_size; i++)
{
if (_array[i] < 0)
{
count++;
}
}
return count;
}
};
int Friends(int n, int m, int r[][2])
{
UnionFindSet FindFriends(n);
for (int i = 0; i < m; i++)
{
FindFriends.Combine(r[i][0], r[i][1]);
}
int friendsCount = FindFriends.FriendCount()-1;
cout << "一共有" << friendsCount << "个朋友圈"<<endl;
return friendsCount;
}
void Test()
{
int r[3][2] = { 1, 2, 2, 3, 4, 5 };
Friends(5, 3, r);
}
2.直接C++写函数
# include<iostream>
using namespace std;
# include"UnionFindSet.h"
//不写类实现并查集方法求朋友圈个数
int FindRoot(int *&array, int child)
{
if (array[child] > 0)
{
return array[child];
}
else
{
return child;
}
}
int FriendsCount(int n,int m,int r[][2])
{
int*array = new int[n];
memset(array, -1, sizeof(int)*n);
for (int i = 0; i < m; i++)//合并两个集合
{
int root1 = FindRoot(array,r[i][0]);
int root2 = FindRoot(array,r[i][1]);
array[root1] += array[root2];
array[root2] = root1;
}
int count = 0;
for (int i = 0; i < n; i++)
{
if (array[i] < 0)
{
count++;
}
}
cout << "朋友圈的个数为:" << count - 1 << endl;
return count - 1;
}
int main()
{
int r[3][2] = { 1, 2, 2, 3, 4, 5 };
FriendsCount(5, 3, r);
}