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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

树形结构 —— 并查集 —— 基本操作

發布時間:2025/3/17 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 树形结构 —— 并查集 —— 基本操作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【初始化】

并查集在最開始時,所有的元素各自單獨構成一個集合。

當集合中只有一元素時,這個集合的代表結點即為該元素,即該元素的父親(father)是其自身。

因此并查集的初始化即將每個元素以自己作為自己的根結點。

int n; int father[N]; void init(){for(int i=1;i<=n;i++)father[i]=i; }

【查詢根結點編號】

查詢根結點編號是并查集中最基礎的用法,即我們給出一個元素編號 x 時,能通過 Find(x) 操作尋找到元素 x 的父結點編號。

1.基本實現

我們知道,一個根結點的父結點編號是其自身,即:father[root]=root,借助這個性質,我們即可找到任意一個結點 x 的根結點。

如下圖,我們知道 father[1]=1,father[2]=1,father[3]=1,father[4]=2,father[5]=2,father[6]=3,假設我們要找 6 號結點的根結點,那么過程為:首先找到 6 號點的父結點為 3 號,然后再找 3 號的父結點為 1 號,再找 1 號的父結點為 1 號,此時發現 1 號點父結點是其自身,即滿足 father[root]=root,說明找到了 6 號點的根結點,即 1 號點

因此,我們利用 while 循環,即可非遞歸地實現 Find(x) 操作

void Find(int x){//非遞歸實現while(father[x]!=x)//未查詢到根結點時x=father[x];//將當前結點更新為其根結點,繼續向上尋找return x; }

由于并查集中每個集合都是一棵樹,那么進一步,我們借助樹可以利用遞歸來完成上述操作

void Find(int x){//遞歸實現if(father[x]!=x)//未查詢到根結點時return Find(father[x]);//以當前結點的父結點進一步查詢return father[x]; }

2.路徑壓縮

可以發現,我們尋找 x 的父結點的過程中,不停的通過 father[x] 數組向上去尋找它的父結點,在這個過程中,相當于把這個結點到其根結點的這條路徑上的所有結點都遍歷了一遍,這無疑加大了時間復雜度。

為解決上述問題,就有了并查集結構中最重要的優化——路徑壓縮,經過路徑壓縮后,使得再次查詢這條路徑上結點的根結點時,只要 O(1) 的時間復雜度即可得到。

路徑壓縮的本質是減少樹的層數,對于一棵集合樹來說,其根結點下依附著諸多結點,在 Find(x) 的過程中,從底向下,我們遞歸的對結點進行更新,如果某個結點不是根結點的話,我們就將這個結點的父結點設為其父結點的父結點,逐層的進行遞歸,盡最大可能的去減少樹的層數。

int Find(int x) {if (x != father[x])return father[x] = Find(father[x]);return father[x]; }

舉例來說,假設初始并查集如下:father[1]=1,father[2]=1,father[3]=2,father[4]=3,father[5]=4,現在我們要進行 Find(4) 操作

根據上述遞歸的路徑壓縮的過程:

可以看出,路徑壓縮完成后,有:father[1]=1,father[2]=1,father[3]=1,father[4]=1,father[5]=4,從 4 號點到 1 號點的這條路徑被壓縮了,當下一次查詢,只需要經過一次詢問,即可得到相應結點的根結點。

【合并兩集合】

合并兩集合同樣是并查集中常見的操作。

對于分屬兩個集合中的元素 x、y,首先利用 Find() 操作,找出兩個結點的根結點 fx、fy,然后判斷根結點是否相同,若相同,說明兩結點已經處于一個集合里,不需合并,若不同,將元素 y 的根結點 fy 指向 x 的根結點 fx 即可

void Union(int x,int y) {int fx=Find(x);//x的根結點int fy=Find(y);//y的根結點if(fx!=fy)//判斷fx與fy是否為一個根結點father[fy]=fx; }

【判斷兩元素是否屬于同一集合】

判斷兩元素是否屬于同一集合,只需要利用 Find() 操作找出兩個元素的根結點 fx、fy,判斷是否相等即可。

bool judge(int x,int y){int fx=Find(x);int fy=Find(y);if(fx==fy)return true;return false; }

【統計集合數目】

在初始化時,我們將每個元素以自己作為自己的根結點,因此當需要統計集合數目時,只需要統計有多少個元素的根結點是其自身即可。

int countSets(int n){int cnt=0;for(int i=1;i<=n;i++)if(father[i]==i)cnt++;return cnt; }

【統計每個集合中元素個數】

統計每個集合中元素的個數需要兩步:首先,對于 n 個結點,我們要先尋找他們的父結點,借助桶排來統計父結點下的結點個數,然后,再統計父結點外的點,即對于每個元素,將其個數記為其父結點下的元素個數。

void countElements(){for(int i=1;i<=n;i++){father[i]=Find(i);//尋找每個節點的父結點num[father[i]]++;//統計父結點下的節點個數}for(int i=1;i<=n;i++)//統計父節點外的點的個數num[i]=num[father[i]];//對于每個元素,將其個數記為其父結點下的元素個數 }

總結

以上是生活随笔為你收集整理的树形结构 —— 并查集 —— 基本操作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。