并查集(加权规则、折叠规则)
1.基本知識概念
等價類與并查集。如果用符號“=”表示集合上的等價關系,那么對于該集合中的任意對象x,y,z,下列性質成立 :
自反性:x=x(即等于自身)
對稱性:若x=y,則y=x
傳遞性:若x=y且y=z,則x=z
一個集合S中的所有對象可以通過等價關系劃分為若干個互不相交的子集S1,S2,S3,…,它們的并集就是S,這些子集即為等價類。
確定等價類的方法:
第一步:讀入并儲存所有的等價對(i,j);
第二步:標記和輸出所有的等價類。
2.解決步驟分析:
假定給定集合:S={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
及以下等價對:
①0=4
②3=1
③6=10
④8=9
⑤7=4
⑥6=8
⑦3=5
⑧2=11
⑨11=0
我們以等式左邊的值為根,最后一棵樹是一個等價類,如果兩個結點的根節點相同,就是在一個等價類。
如下解決過程:
如下圖,黑色的為單個等價類,其他顏色相同的為一個等價類,我們以等號左邊的作為根:
處理完⑤-⑧后的結果 :
處理完⑨后的結果 :
3.解決代碼:
3.1正常普通解決代碼:
class UFsets { private:int* parent;unsigned int size; public:UFsets(unsigned int sz){size = sz;parent = (int*)malloc(sizeof(int) * sz);for (int i = 0; i < size; i++){parent[i] = -1;}}~UFsets(){free(parent);}bool Union(int a, int b){bool res = false;int ra = FindParent(a);int rb = FindParent(b);if (ra != rb){parent[ra] += parent[rb];parent[rb] = ra;res = true;}return res;}int FindParent(int ra){while (parent[ra] >= 0){ra = parent[ra];}return ra;}void Print(int ri){printf("%3d ", ri);for (int i = 0; i < size; i++){if (parent[i] == ri){Print(i);}}}void PrintSet(){int s = 0;for (int i = 0; i < size; i++){if (parent[i] < 0){printf("第 %d 集合\n", ++s);Print(i);printf("\n");}}} };int main() {UFsets set(12);set.Union(0, 4);set.Union(3, 1);set.Union(6, 10);set.Union(8, 9);set.Union(7, 4);set.Union(6, 8);set.Union(3, 5);set.Union(2, 11);set.Union(11, 0);return 0; }調試查看parent值發現和我們分析時最后的數組結果一樣(我統一以等價左邊的值作為根):
用類的PrintSet()打印各個等價類結果:
4.加權規則和折疊規則
但是Find和Union的操作性能不好。假設最初n個元素構成n棵樹組成的森林,parent[i] = -1。做處理Union(n-2,n-1),Union(n-3,n-1),Union(n-4,n-1)…Union(0,n-1)后,將產生如下圖所示的退化的樹:
執行一次Union操作需時間O(1),n-1此是O(n)。若再執行Find(0),Find(1),…,Find(n-1),若被搜索的元素為i,完成Find(i)操作為O(i),完成n次搜索是O(n^2)。
所以在這里提出了加權規則和折疊規則。
4.1加權規則
先判斷兩個集合中的結點個數,如果以i為根的樹中的結點個數少于以j為根的樹中的結點個數,即parent[i] > parent[j],則讓j成為i的雙親,否則,讓i成為j的雙親。以下即為加權規則代碼嗎,將以下代碼添加到class UFsets中即可。
bool WeightedUnion(int a, int b)//加權規則{bool res = false;int ra = FindParent(a);int rb = FindParent(b);if (ra != rb){if (parent[ra] > parent[rb])//說明parent[rb]結點更多,讓rb成為根節點{parent[rb] += parent[ra];parent[ra] = rb;}else//讓ra成為根節點{parent[ra] += parent[rb];parent[rb] = ra;}res = true;}return res;}4.2折疊規則
如果j是從i到根的路徑上的一個結點,并且parent[j] ≠ root,則把parent[i]置于root,意思是讓i的雙親直接為root。
如下即為從i結點向上壓縮路徑,將代碼添加到class UFsets中即可 :
以下為折疊壓縮后的結果:
總結
以上是生活随笔為你收集整理的并查集(加权规则、折叠规则)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是设计模式
- 下一篇: 回溯法——打印子集树