引:例题
小米公司一道面试题:
假如已知有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属于另一个朋友圈,共有两个朋友圈。请写出相应程序。
并查集
定义:
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题。常常在使用中以森林来表示。
集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。
实现:
用小米面试题为例,我们可以用树形结构表示不同的朋友圈。
1,2,3属于一个朋友圈。
4,5属于一个朋友圈。
我们可以用一个大小的6的数组表示。
(这里如果用大小为6的数组,则最后朋友圈个数结果应减1,因为数组下标为0的多加了一次。也可以用大小为5的数组)
数组的初始值全部为-1。
(这里-1,1为此数的结点个数,负号起到一个标识区分的作用。)
当两个人为一个朋友圈时,第一个人下标所在的值为两个下标所在值的和,第二个人的值为第一个人的下标。
当1,2为一个朋友圈时:
当2,3为朋友圈时:
这时查到2下标处的值不为负数,则应该继续查找2的值所对应的下标处,直到找到跟为止。
找到此数字,再将两者相加
其余好友关系同上
最后,统计朋友圈的个数,只需要统计数组中有几个负数即可。
代码实现:
#pragma once
#include <stdio.h>
#include <assert.h>
#include <vector>
using namespace std;
class Union_Find_Sets
{
public:
Union_Find_Sets(size_t size)
{
s.resize(size, -1);
}
int FindRoot(int x)
{
int root = x;
while (s[root] >= 0)
{
root = s[root];
}
return root;
}
void Union(int x1,int x2)
{
//先找X1与X2的根
int root1 = FindRoot(x1);
int root2 = FindRoot(x2);
//如果两个元素在同一集合中 则相加会出错
if (root1 != root2)
{
s[root1] += s[root2];
s[root2] = root1;
}
}
//看两个结点是否属于同一集合 只需要看两个跟是否相同
bool IsSameSet(int x1,int x2)
{
return FindRoot(x1) == FindRoot(x2);
}
//看有多少集合数目 只需要看非负数的个数
size_t SetSize()
{
size_t count = 0;
for (size_t idx = 0; idx < s.size(); idx++)
{
if (s[idx] < 0)
count++;
}
return count;
}
private:
vector<int> s;
};
void Test()
{
Union_Find_Sets st(10);
st.Union(0, 3);
st.Union(6, 8);
st.Union(3, 6);
st.Union(4, 1);
st.Union(1, 5);
st.Union(2, 7);
st.Union(7, 9);
cout<<st.IsSameSet(0, 9)<<endl;
cout<<st.SetSize()<<endl;
}