圖的連通性問題:無向圖的連通分量和生成樹,所有頂點均由邊連接在一起,但不存在回路的圖。


設圖 G=(V, E) 是個連通圖,當從圖任一頂點出發遍歷圖G 時,將邊集 E(G) 分成兩個集合 T(G) 和 B(G)。其中 T(G)是遍歷圖時所經過的邊的集合,B(G) 是遍歷圖時未經過的邊的集合。顯然,G1(V, T) 是圖 G 的極小連通子圖,即子圖G1 是連通圖 G 的生成樹。
深度優先生成森林
右邊的是深度優先生成森林:
最小生成樹
給定一個無向網絡,在該網的所有生成樹中,使得各邊權數之和最小的那棵生成樹稱為該網的最小生成樹。
問題的提出:要在 n 個城市間建立交通網,要考慮的問題如何在保證 n 點連通的前題下最節省經費?
如何求連通圖的最小生成樹?
構造最小生成樹的算法很多,其中多數算法都利用了一種稱之為 MST 的性質。
MST 性質:設 N = (V, E) 是一個連通網,U 是頂點集 V 的一個非空子集。若邊 (u, v) 是一條具有最小權值的邊,其中u∈U,v∈V-U,則必存在一棵包含邊 (u, v) 的最小生成樹。
方法一:普里姆 (Prim) 算法。
算法思想:
小生成樹。
總得來說,普里姆算法就是以樹為單位,找最小的權邊,特點是針對無向圖!只和頂點有關,和邊無關,適用於稠密圖。算法時間復雜度為 O(n^2)
如圖:普里姆算法求最小生成樹

初始令 U={u0}, (u0屬於V ), TE={ }。



繼續

最后,遍歷完

Prim算法的實現
方法二:克魯斯卡爾 (Kruskal) 算法。
算法思想:
最小生成樹可能不惟一(包括普里姆算法都是一樣的道理)
使用並查集可以判斷是否形成了回路,kruskal算法用到了一種貪心策略,首先要把邊集數組以邊的權值從小到大排序,然后一條邊一條邊的查找,如果邊的兩個端點不在一個集合內,則將此邊添加到正在生長的樹林中,並合並兩個端點所在的集合,直到最小生成樹已生成完畢。
並查集是一種非常簡單的數據結構,它主要涉及兩個基本操作,分別為:
A. 合並兩個不相交集合
B. 判斷兩個元素是否屬於同一個集合
1)合並兩個不相交集合(Union(x,y))
合並操作很簡單:先設置一個數組Father[x],在克魯斯卡爾算法里,需要使用雙親存儲結構,表示x的“父親”的編號。那么,合並兩個不相交集合的方法就是,找到其中一個集合最父親的父親(也就是最久遠的祖先),將另外一個集合的最久遠的祖先的父親指向它。
通俗的說,就是把其中一個樹的根,作為另一個樹的根結點的一個孩子結點即可。

上圖為兩個不相交集合,合並后可以看出:Father(b)=Father(g)=f 結點
2)判斷兩個元素是否屬於同一集合(Find_Set(x)),本操作可轉換為尋找兩個元素的最久遠祖先是否相同。可以采用遞歸實現。
並查集的優化問題
尋找祖先時,我們一般采用遞歸查找,但是當元素很多亦或是整棵樹變為一條鏈時,每次Find_Set(x)都是O(n)的復雜度。為了避免這種情況,我們需對路徑進行壓縮,即當我們經過”遞推”找到祖先節點后,”回溯”的時候順便將它的子孫節點都直接指向祖先,這樣以后再次Find_Set(x)時復雜度就變成O(1)了,如下圖所示。可見,路徑壓縮方便了以后的查找。

回到克魯斯卡爾算法,使用並查集來實現判斷回路的生成否
比如從 v1開始(一共是 v1、v2、v3、v4、v5、v6),則開始把 v1-v6作為各個單根樹,以森林來表示,讓每個元素構成一個個的單元素的集合,需要使用數組表示,存儲方式就是雙親存儲結構(方便找到共同的父親)。

每次使用並查集,將后入的邊上的另一個結點作為孩子結點,而沒有加入的結點還是去做為單根的樹:

如圖所示,上圖,該選取權值=5的邊了,此時有兩個樹
和 
如果選取3-4或者1-4這兩條邊的任意一個,單根樹是不會產生根相同的情形的,而加入的(作為孩子的根),一定會找到共同祖先的,這樣就可以發現回路的存在! 而選取2-3這條邊的話,在並查集中,就不會查出共同的祖先,也就是沒有環的形成。

通俗的說,就是通過兩個元素所在的結點推出跟結點,若根相同,則為同一個集合,否則不是同一個集合(也就是不形成回路)
歡迎關注
dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

