圖的連通性問題:無向圖的連通份量和生成樹,全部頂點均由邊鏈接在一塊兒,但不存在迴路的圖。算法
設圖 G=(V, E) 是個連通圖,當從圖任一頂點出發遍歷圖G 時,將邊集 E(G) 分紅兩個集合 T(G) 和 B(G)。其中 T(G)是遍歷圖時所通過的邊的集合,B(G) 是遍歷圖時未通過的邊的集合。顯然,G1(V, T) 是圖 G 的極小連通子圖,即子圖G1 是連通圖 G 的生成樹。數組
深度優先生成森林網絡
右邊的是深度優先生成森林:數據結構
最小生成樹
給定一個無向網絡,在該網的全部生成樹中,使得各邊權數之和最小的那棵生成樹稱爲該網的最小生成樹。優化
問題的提出:要在 n 個城市間創建交通網,要考慮的問題如何在保證 n 點連通的前題下最節省經費? spa
如何求連通圖的最小生成樹?3d
構造最小生成樹的算法不少,其中多數算法都利用了一種稱之爲 MST 的性質。blog
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開始(一共是 v一、v二、v三、v四、v五、v6),則開始把 v1-v6做爲各個單根樹,以森林來表示,讓每一個元素構成一個個的單元素的集合,須要使用數組表示,存儲方式就是雙親存儲結構(方便找到共同的父親)。
每次使用並查集,將後入的邊上的另外一個結點做爲孩子結點,而沒有加入的結點仍是去作爲單根的樹:
如圖所示,上圖,該選取權值=5的邊了,此時有兩個樹
和
若是選取3-4或者1-4這兩條邊的任意一個,單根樹是不會產生根相同的情形的,而加入的(做爲孩子的根),必定會找到共同祖先的,這樣就能夠發現迴路的存在! 而選取2-3這條邊的話,在並查集中,就不會查出共同的祖先,也就是沒有環的造成。
通俗的說,就是經過兩個元素所在的結點推出跟結點,若根相同,則爲同一個集合,不然不是同一個集合(也就是不造成迴路)