並查集的“並優化”(leader合併)和“查優化”(路徑壓縮)
阿新 • • 發佈:2019-02-18
在博文http://blog.csdn.net/stpeace/article/details/46506861中, 我們已經詳細地瞭解了並查集, 不過, 那個程式略顯粗糙, 下面我們考慮來優化一下。
先給出沒有優化的程式碼吧:
結果為:// 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
實際上, 在findLeader的時候, 我們可以進行路徑壓縮, 這是“查優化”的關鍵點。而在並的過程中, 也可以進行“並優化”, 不過, “並優化”的作用不太明顯, 如下:
結果同樣是:// 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]; // 沒找到的話, 一直往上找 } // "查優化"的本質是路徑壓縮, 最終使得所有員工的直接上司均為該組的leader int i = n; int j = 0; while(i != r) { j = leader[i]; leader[i] = r; i = j; } return r; } // 將兩個領導帶領的團隊融合, 從此, leaderX和leaderY建立了新的統一戰線, 是一個大家庭團隊了 void uniteSet(int leaderX, int leaderY) { // 我個人認為:"並優化"的作用不是很大 if(leaderX < leaderY) { leader[leaderX] = leaderY; } else { 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/stpeace/article/details/46506861