並查集的簡介及其C/C++代碼的實現(某公司招聘筆試試題)


       當年, 我在一個公司實習, 某次, 在一次算法交流的過程中, 我第一次聽到了並查集這個看似高大上的概念, 也再一次感覺到了自己的無知。 

       對於一個非計算機專業的人來說, 你跟他說並查集, 就有點像你對着計算機專業的人說Gibbs現象或者FFT一樣, 你懂的。 后來, 某公司的招聘筆試題目中, 又出現並查集, 讓我們一起來看看這個題目吧:


       假如已知有 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 個朋友圈。

       其實, 這是個並查集的問題, 比較簡單。 


       下面, 我們來寫個並查集的程序玩玩, 加深對並查集的理解:

// taoge的並查集

#include <iostream>
using namespace std;

#define N 1000
int leader[N + 1] = {0}; // 先搞一個充分大的數組

// 初始化
void setLeader()
{
int i = 1;
for(i = 1; i <= N; i++)
{
leader[i] = i; // 初始化時, 將自己初始化為自己的領導
}
}

// 查找領導, 看看究竟是誰(實際上, 還可以進行路徑壓縮優化)
int findLeader(int n)
{
int r = n;
while(leader[r] != r)
{
r = leader[r]; // 沒找到的話, 一直往上找
}

return r;
}

// 將兩個領導帶領的團隊融合, 從此, leaderX和leaderY建立了新的統一戰線, 是一個大家庭團隊了
void uniteSet(int leaderX, int leaderY)
{
leader[leaderX] = leaderY; // leader[leaderY] = leaderX;
}

// 輸入數組, 每一行表示一個集合關系, 比如第一行表示3和4屬於一個集合團隊
int input[] =
{
3, 4,
4, 2,
7, 6,
5, 1,
3, 9,
11, 8,
6, 10,
9, 13,
11, 12,
};

// 測試數組, 測試每行的兩個整數是否屬於同一個大的家庭團隊
int test[] =
{
3, 2,
9, 4,
7, 10,
6, 7,
13, 4,
8, 12,

6, 9,
4, 7,
11, 10,
1, 2,
12, 13,
7, 13,
};


int main()
{
int numberOfSets = 13; // 總共有13個元素, 即1, 2, 3, 4, ...., 13

// 初始化領導
setLeader();

int i = 0;
int j = 0;
int n = sizeof(input) / sizeof(input[0]) / 2;
for(j = 0; j < n; j++)
{
int u = input[i++];
int v = input[i++];

// 找領導
u = findLeader(u);
v = findLeader(v);

// 領導不相等, 則融合着兩個團隊, 合二為一
if(u != v)
{
uniteSet(u, v);
numberOfSets--;
}
}

i = 0;
n = sizeof(test) / sizeof(test[0]) / 2;
for(j = 0; j < n; j++)
{
int u = test[i++];
int v = test[i++];

// 找領導
u = findLeader(u);
v = findLeader(v);

// 如果領導不相同, 則不屬於一個團隊; 如果兩個領導相同, 則肯定屬於一個團隊
if(u != v)
{
cout << "NO" << endl;
}
else
{
cout << "YES" << endl;
}
}


// 其實, 經合並后, 最后的集合是4個:
// {3, 4, 2, 9, 13}, {7, 6, 10,}, {5, 1}, {11, 8, 12}
cout << numberOfSets << endl;

return 0;
}
      結果為:

YES
YES
YES
YES
YES
YES
NO
NO
NO
NO
NO
NO
4


       其實, 並查集很簡單, 無非就是查查並並的操作。 不過, 並查集的思想, 確實很優秀。 要說明的是, 上述代碼其實可以優化, 比如路徑壓縮等。



       如果大家覺得上述程序不太好理解, 那就請參考:http://blog.csdn.net/dellaserss/article/details/7724401這篇博文,那篇博文是轉載的, 寫的通俗易通, 形象生動,可讀性強。 最后, 我把那篇文章的一個圖借鑒過來, 欣賞一下, 挺有意思的(在此, 特別感謝下圖的原作者羡慕):



       OK,  本文先到此為止。






注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com