小米麵試題---朋友圈問題(並查集)
阿新 • • 發佈:2019-01-07
假如已知有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屬於另一個朋友圈,結果為兩個朋友圈。
這道題有很多種解法,首先想到的就是定義一個數組,陣列元素也是陣列。使用STL庫,定義這樣的一個結構:
vector<vector<int>> _v;
然後遍歷每對好友關係,如果陣列中有相同元素(好友),就在當前陣列中新增該好友,最後,遍歷最外層陣列元素個數就可以知道有多少個朋友圈了。
作為小米麵試題,它不僅要求正確性還有效率,上面給出的解法也能做出這道題,但是效率太低,所以,在這裡,我使用一種更高效,快捷的資料結構-----並查集。
並查集:
並查集是一種樹型的資料結構,用於處理一些不相交集合(Disjoint Sets)的合併及查詢問題。常常在使用中以森林來表示。集就是讓每個元素構成一個單元素的集合,也就是按一定順序將屬於同一組的元素所在的集合合併。在一些有N個元素的集合應用問題中,通常是在開始時讓每個元素構成一個單元素的集合,然後按一定順序將屬於同一組的元素所在的集合合併,其間要反覆查詢一個元素在哪個集合中。
如圖所示:
查詢根:
int GetRoot(int root) { if(_unionset[root] >= 0) //_unionset[]為負數時找到 { root = _unionset[root]; } return root; }
合併朋友圈(判斷x1與x2是否已經為好友關係,不是則併合併到所在朋友圈):
void Union(int x1,int x2)
{
int root1 = GetRoot(x1);
int root2 = GetRoot(x2);
if(root1 != root2)
{
_unionset[root1] += _unionset[root2];
_unionset[root2] = root1;
}
}
計算朋友圈個數:
int Count() { int count = 0; for(int i = 0; i<_n; ++i) { if(_unionset[i] < 0) count++; } return count-1;//因為0號位置不用,但初始化時初始化為了-1,需要減去多算的這一個 }
整體程式碼:
#pragma once
#include <iostream>
using namespace std;
#include <vector>
#include <cassert>
class UnionSet
{
public:
UnionSet(int n)
:_n(n)
{
_unionset = new int[n];
//memset(_unionset,-1,sizeof(int)*n);memset按位元組處理,只能初始化為0,1,-1
//安全起見,都用for迴圈處理
for(int i = 0; i<n; ++i)
{
_unionset[i] = -1;
}
}
int GetRoot(int root)
{
if(_unionset[root] >= 0)
{
root = _unionset[root];
}
return root;
}
void Union(int x1,int x2)
{
int root1 = GetRoot(x1);
int root2 = GetRoot(x2);
if(root1 != root2)
{
_unionset[root1] += _unionset[root2];
_unionset[root2] = root1;
}
}
bool Find(int x1,int x2)
{
int root1 = GetRoot(x1);
int root2 = GetRoot(x2);
return root1 == root2;
}
int Count()
{
int count = 0;
for(int i = 0; i<_n; ++i)
{
if(_unionset[i] < 0)
count++;
}
return count-1;
}
protected:
//vector<int> v;
int* _unionset;
int _n;
};
int Friend(int n,int m,int r[][2])
{
assert(r);
UnionSet u(n+1);//多開一個空間,0號位置不用
for(int i = 0;i<m;++i)
{
int r1 = r[i][0];
int r2 = r[i][1];
u.Union(r1,r2);
}
return u.Count();
}
Test.cpp
void Test1()
{
int r[4][2] = {{1,2},{2,3},{4,5},{1,3}};//n=總人數,m=多少對好友關係
cout<<"朋友圈?"<<Friend(5,4,r)<<endl;
}
void Test2()
{
int r[][2] = {{1,2},{2,3},{4,5},{5,9},{6,2},{7,8}};//n=總人數,m=多少對好友關係
cout<<"朋友圈?"<<Friend(9,6,r)<<endl;
}