算法导论之图的最小生成树
引出最小生成樹,是提到電子線路設計時,要把數個元件的引腳連接在一起,使其電位相同。使n個引腳互相連通,可以使用n-1條連接線,每條連接線連接兩個引腳。尋求連接線最少的方案,是最小生成樹的應用。將電子線路引腳接線連接問題模型化求解一個無向帶權連通圖的頂點互聯最小代價。
一個無向帶權連通圖G=(V,E),其中V是引腳集合(頂點),E是每個引腳之間可能互聯的集合(邊)。圖中每一條邊(u,v)∈E,都有一個權值w=(u,v)表示連接u和v的代價(引腳間接線數目)。
從這樣一個圖中找出一個無回路的子集T?E,這個子集T連接了所有頂點,且其邊權值之和最小。
T無回路且連接所有頂點,是一個樹,成為生成樹,權值最小,即是最小生成樹。求解圖G的T子集的問題就是最小生成樹問題。如何從一個圖中生成一顆最小生成樹,主要有Kruskal和Prim兩個算法,時間性能都是O(ElgV)。其中Prim算法通過采用斐波那契堆可將運行時間減少到O(E+VlogV),適用于|V|遠小于|E|的圖求解最小生成樹。
不得不說的,兩個算法的思想核心是貪心算法。在算法的每一步中,都必須在幾種可能性中選擇一種,動態規劃是都選擇并求解最優子解最后組合成最優解;而貪心算法則是選擇可能性中最佳的一種來求解最優子解。一般來說,貪心算法這種策略不能保證找到全局最優解,但在最小生成樹問題上,通過貪心策略可以獲得最小權值的生成樹是可證的。
通用最小生成樹算法:
假設已知一個無向連通圖G=(V,E),其權值函數w:E->R,目的是找出圖G的最小生成樹。
通用最小生成樹算法采用貪心策略,在每一個步驟都形成最小生成樹的一條邊。算法維護一個邊集合A,保持循環不變式:
在每一次循環迭代之前,A是某個最小生成樹的一個子集。
在算法的每一步中,確定一條邊(u,v),是的將它加入集合A后,仍然不違反這個循環不變式,即AU{(u,v)}仍然是某一個最小生成樹的子集,這樣的邊(u,v)為A的安全邊。安全邊加入子集A后,A仍然保持是某一個最小生成樹的子集。
這是貪心算法思想,每一步尋找可能解中最優。那問題就是,怎么尋找安全邊,確保加入A后使A仍然是最小生成樹的子集。
算法導論中給出一個識別安全邊的規則,適用于無向圖。先給出這個規則的定理。
設圖G=(V,E)是一個無向連通圖,并且在E上定義了一個具有實數值的加權函數w。設A是E的一個子集,它包含于G的某個最小生成樹中。設割(S,V-S)是G的任意一個不妨害A的割,且邊(u,v)是通過割(S,V-S)的一條輕邊,則邊(u,v)對集合A來說是安全的。
這個定理中關鍵有三點,第一:將圖分為兩個子圖S和V-S,即為割(S,V-S);第二:割(S,V-S)的邊(u,v)是輕邊,就是連接子圖S和V-S所有邊中最小權值的一條;第三:已經在子集A的邊不能是連接割的邊,稱之為不妨害A的割;
算法導論中證明識別安全邊的規則就不具體展開,主要是構造一個通用圖,不失為普通的情況來證明。通俗地理解就是:將圖分割成兩個子圖,兩個子圖間的聯系的邊中最小權值的就是安全邊,前提是這些邊不能是集合A中的。這里按歸納法證明下:
假設圖G=(V,E)是一個無向連通圖,
割(1,V-1),那1個頂點和V-1個頂點兩個子圖之間最小權值的邊就是安全邊了,A集合中有1個頂點,無邊;
割(2,V-2),引入第二個頂點時,2和V-2割之間的邊顯然不會妨害A集合,找出兩個割之間的最小權值也容易,加入安全邊,集合A有一條邊,就是(1,2);
如此類推,n個頂點加入時,割(n,V-n)中n個頂點之間的構成子圖就是集合A的輕邊,就是最小生成樹。通用算法之上改良的有Kruskal和Prim算法。
Kruskal算法將集合A(最小生成樹的頂點集和邊集)是一個森林,加入集合A中的安全邊總是途中連接兩個不同連通分支的最小權邊。而Prim算法則將集合A形成單顆樹,加入集合A的安全邊總是連接樹與一個不在樹中的頂點的最小權邊。
實際,二者思想是一致,只不過通過不同的數據結構來實現,時間性能分析中還涉及到增長極為緩慢的函數。Prim算法應用二叉最小堆和斐波那契堆保存最小優先隊列Q也有不同的性能效果。
Kruskal算法描述如下:
1)圖G=(V,E)中的每個頂點都是一棵樹,這樣初始有V顆樹,按照E的權值大小排序邊集。初始化最小生成樹,即集合A為空集;
2)按順序找出權限的邊集中的安全邊,如果該邊的兩個頂點屬于不同樹,那么可以合并兩棵樹,并把邊加入到集合A中;
3)直到所有的頂點都已在集合A中,其邊權值最小。
核心思想就是貪心算法的,找出森林中權值的最小的邊,然后合并該邊聯系的兩棵樹。或者從因到果的過程來看,要找出最小生成樹,我就把所有邊按順序排序,依次將最小權值的邊納入集合A中。
Prim算法描述如下:
1)集合A是一顆正在成長的單棵樹,樹從任意頂點開始,逐漸生成直到覆蓋所有頂點;
2)生成過程是將連接樹A和G-A中最小權值的邊加入A;
通俗地說,就是從圖中的任意一個頂點出發,找到該頂點所有的邊,把最小權值的邊放入集合A中即可。實現該算法,要借助一個變量:最下優先級隊列Q。Q保存所有頂點中與頂點相連的邊中的最小權值。Q二叉最小堆實現,時間性能和Krusal算法一樣,但若使用斐波那契堆可以改善。
兩個算法基本都是將最小權值的邊找出來,然后將安全邊的頂點加入集合A中,從而逐步構成一棵正在成長的最小生成樹。
這里給出平攤分析中提到的增長極快函數及其增長極慢的逆函數,有助于理解在時間性能分析中引入增長極慢函數的意義。增長極快的函數:
10的80次方已經是可觀察到的宇宙中估計的原子數量。
有點像2的64次方的故事,數學的魔力在于此,簡單這樣的一個函數設計,只要k=4就已經大到無邊,大到目今我們所認知宇宙的數量極限。
對應增長極慢的函數,就是該函數的逆函數。對整數j≥0,定義函數Ak(j)的逆函數為:
a(j)=min{k: Ak(1)≥j}
a(j)是的函數Ak(1)至少為j的最低級別k。根據增長極快函數Ak(1)的值,可知:
a(j)=0,當0≤j≤2
a(j)=1,當j=3
a(j)=2,當4≤j≤7
a(j)=3,當8≤j≤2047
a(j)=4,當2048≤j≤A4(1)
可見如此,在實際應用中的數字一般都是只有a(j)≤4,是一個增長極為緩慢的函數,變量超級無敵大,我依舊小于4。讓人不禁想起阿基米德的豪言壯語:給我一個支點,我就能支起地球。宇宙之極大又或者是極小,未可知啊。總結
以上是生活随笔為你收集整理的算法导论之图的最小生成树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java实现算法导论中图的广度优先搜索(
- 下一篇: MapReduce基础开发之十读写ORC