日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构 之 并查集

發(fā)布時間:2025/6/15 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构 之 并查集 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

并查集是一種樹型的數(shù)據(jù)結(jié)構(gòu),其保持著用于處理一些不相交集合(Disjoint Sets)的合并及查詢問題。

有一個聯(lián)合-查找算法union-find algorithm)定義了兩個操作用于此數(shù)據(jù)結(jié)構(gòu):

  • Find:確定元素屬于哪一個子集。它可以被用來確定兩個元素是否屬于同一子集。
  • Union:將兩個子集合并成同一個集合。
下面代碼實現(xiàn)一些并查集中的一些基本操作: 一、并查集的初始化 假設(shè)現(xiàn)在有一個全集合為S= {0,1,2,3,4,5,6,7,8,9} ,初始化時,將每一元素都劃分成為一個單元素的集合。 如下圖所示: 這樣就初始化出10個單元素集合,每一個集合元素的parent值都是-1.
代碼實現(xiàn)過程: [cpp]?view plaincopy
  • int?parent[100];??
  • ??
  • //構(gòu)造一個初始并查集,s是集合元素的個數(shù),此時初始化了s個集合,每一個集合都只有一個元素??
  • //數(shù)組的范圍為parent[0]~parent[s-1]??
  • //初始的時候,數(shù)組元素的值都是?-1,表示此時都是根結(jié)點。??
  • void?ufset(int?s)??
  • {??
  • ????int?si=s;??
  • ????for(int?i=0;i<si;i++)??
  • ????parent[i]=-1;??
  • }??

  • 二、合并Union 所謂合并,即是將兩個不相交的集合合并成為一個集合,這個過程將一個集合的parent修改成另一集合的parent。 代碼實現(xiàn)過程: [cpp]?view plaincopy
  • //集合的合并??
  • //讓root2的父指針指向root1即可實現(xiàn)兩個集合的合并??
  • void?Union(int?root2,int?root1)??
  • {??
  • ????parent[root1]+=parent[root2];??
  • ????parent[root2]=root1;??
  • }??

  • 舉個例子:初始化10個元素(如上圖所示),進(jìn)行下面的合并過程: [cpp]?view plaincopy
  • ufset(10);??//初始化10個元素??
  • ??
  • ????Union(6,0);??
  • ????Union(7,0);??
  • ????Union(8,0);??
  • ????Union(4,1);??
  • ????Union(9,1);??
  • ????Union(3,2);??
  • ????Union(5,2);??

  • 合并之后的結(jié)果:

    但是進(jìn)行這種合并之后,可能會出現(xiàn)一種不好的效果。如下圖:
    我們稱之為一棵退化的樹。

    那么如何進(jìn)行改進(jìn)呢?我們可以使用加權(quán)規(guī)則進(jìn)行合并。也就是將集合元素少的歸到集合元素多的中去。 例如:
    下面給出代碼實現(xiàn):(注意下面這個方法存在錯誤,請看下面第二個方法,已修正錯誤。) [cpp]?view plaincopy
  • //使用加權(quán)規(guī)則得到改進(jìn)的Union操作??
  • void?WeightUnion(int?root2,int?root1)??
  • {??
  • ????int?r2=Find(root2);??//r2和r1是root2和root1的父結(jié)點??
  • ????int?r1=Find(root1);??
  • ????int?temp;??
  • ????if(r1!=r2)??
  • ????{??
  • ????????temp=parent[r1]+parent[r2];??
  • ????????if(parent[r1]<parent[r2]){parent[r1]=temp;parent[r2]=r1;}??//以r2根的樹結(jié)點多??
  • ????????else?{parent[r1]=r2;parent[r2]=temp;}??
  • ????}??
  • }??

  • 錯誤提醒:很感謝某位網(wǎng)友指出我上面段代碼的錯誤,由于一時疏忽上面WeightUnion這個方法有點錯誤!修改如下:
    [cpp]?view plaincopy
  • //使用加權(quán)規(guī)則得到改進(jìn)的Union操作??
  • void?WeightUnion(int?root2,int?root1)??
  • {??
  • ????int?r2=Find(root2);??//r2和r1是root2和root1的父結(jié)點??
  • ????int?r1=Find(root1);??
  • ????int?temp;??
  • ????temp=parent[r1]+parent[r2];??
  • ????if(parent[r1]<=parent[r2])???
  • ????//注意:parent[r1]?和?parent[r2]?都是負(fù)數(shù),所以小者其絕對值大,那么擁有的結(jié)點數(shù)就多??
  • ????//這里就說明?r1為根的樹結(jié)點??不少于??r2為根的樹結(jié)點??
  • ????{????
  • ????????parent[r1]=temp;????//以r1為根???
  • ????????parent[r2]=r1;????
  • ????}????
  • ????else????
  • ????{????
  • ????????parent[r1]=r2;????
  • ????????parent[r2]=temp;????
  • ????}??????
  • }??



  • 三、查找Find
    我們知道,只有當(dāng)查找到的元素的parent值為負(fù)數(shù)(此時集合元素個數(shù)用這個負(fù)數(shù)表示),才表示找到根。 [cpp]?view plaincopy
  • //查找元素x所在集合??
  • //從x開始,沿父指針鏈一直向上,直到向上,直到達(dá)到一個父指針域為負(fù)值的結(jié)點位置??
  • int?Find(int?x)?//迭代查找方式??
  • {??
  • ????while(parent[x]>=0)?x=parent[x];??
  • ????return?x;??
  • }??
  • /*?
  • int?find_1(int?x)??//遞歸查找方式?
  • {?
  • ????if(parent[x]<0)?return?x;??//x是根時,直接返回x?
  • ????else?return?find_1(parent[x]);??//否則,遞歸找x的父的根?
  • }?
  • */??

  • 進(jìn)一步優(yōu)化,在查找過程中可以采用折疊規(guī)則壓縮路徑。

    實現(xiàn)的代碼: [cpp]?view plaincopy
  • //折疊規(guī)則壓縮路徑法??
  • //包含元素i的樹中搜索根,并將從元素i到根的路徑上的所有結(jié)點都變成根的結(jié)點??
  • int?collapsingfind(int?i)??
  • {??
  • ????int?j;??
  • ????for(j=i;parent[j]>=0;j=parent[j]);?//搜索j的根??
  • ????while(i!=j)???//向上逐次壓縮??
  • ????{??
  • ????????int?temp=parent[i];??
  • ????????parent[i]=j;??
  • ????????i=temp;??
  • ????}??
  • ????return?j;??//返回根??
  • }??


  • 附:上面代碼的完整版: [cpp]?view plaincopy
  • #include?<iostream>??
  • using?namespace?std;??
  • ??
  • int?parent[100];??
  • ??
  • //構(gòu)造一個初始并查集,s是集合元素的個數(shù),此時初始化了s個集合,每一個集合都只有一個元素??
  • //數(shù)組的范圍為parent[0]~parent[s-1]??
  • //初始的時候,數(shù)組元素的值都是?-1,表示此時都是根結(jié)點。??
  • void?ufset(int?s)??
  • {??
  • ????int?si=s;??
  • ????for(int?i=0;i<si;i++)??
  • ????parent[i]=-1;??
  • }??
  • ??
  • //查找元素x所在集合??
  • //從x開始,沿父指針鏈一直向上,直到向上,直到達(dá)到一個父指針域為負(fù)值的結(jié)點位置??
  • int?Find(int?x)?//迭代查找方式??
  • {??
  • ????while(parent[x]>=0)?x=parent[x];??
  • ????return?x;??
  • }??
  • /*?
  • int?find_1(int?x)??//遞歸查找方式?
  • {?
  • ????if(parent[x]<0)?return?x;??//x是根時,直接返回x?
  • ????else?return?find_1(parent[x]);??//否則,遞歸找x的父的根?
  • }?
  • */??
  • ??
  • //折疊規(guī)則壓縮路徑法??
  • //包含元素i的樹中搜索根,并將從元素i到根的路徑上的所有結(jié)點都變成根的結(jié)點??
  • int?collapsingfind(int?i)??
  • {??
  • ????int?j;??
  • ????for(j=i;parent[j]>=0;j=parent[j]);?//搜索j的根??
  • ????while(i!=j)???//向上逐次壓縮??
  • ????{??
  • ????????int?temp=parent[i];??
  • ????????parent[i]=j;??
  • ????????i=temp;??
  • ????}??
  • ????return?j;??//返回根??
  • }??
  • ??
  • //集合的合并??
  • //讓root2的父指針指向root1即可實現(xiàn)兩個集合的合并??
  • void?Union(int?root2,int?root1)??
  • {??
  • ????parent[root1]+=parent[root2];??
  • ????parent[root2]=root1;??
  • }??
  • ??
  • //使用加權(quán)規(guī)則得到改進(jìn)的Union操作??
  • void?WeightUnion(int?root2,int?root1)??
  • {??
  • ????int?r2=Find(root2);??//r2和r1是root2和root1的父結(jié)點??
  • ????int?r1=Find(root1);??
  • ????int?temp;??
  • ????if(r1!=r2)??
  • ????{??
  • ????????temp=parent[r1]+parent[r2];??
  • ????????if(parent[r1]<parent[r2]){parent[r1]=temp;parent[r2]=r1;}??//以r2根的樹結(jié)點多??
  • ????????else?{parent[r1]=r2;parent[r2]=temp;}??
  • ????}??
  • }??
  • ??
  • int?main()??
  • {??
  • ????ufset(10);??//初始化10個元素??
  • ??
  • ????Union(6,0);??
  • ????Union(7,0);??
  • ????Union(8,0);??
  • ????Union(4,1);??
  • ????Union(9,1);??
  • ????Union(3,2);??
  • ????Union(5,2);??
  • ??
  • ????for(int?i=0;i<10;i++)?cout<<parent[i]<<"?";??
  • ????return?0;??
  • } ?
  • 總結(jié)

    以上是生活随笔為你收集整理的数据结构 之 并查集的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。