演算法導論 第23章 最小生成樹

2021-06-22 17:24:00 字數 3980 閱讀 7357

最小生成樹(minimum spanning tree),全稱「最小權值生成樹」。在含有n個頂點的連通圖中選擇n-1條邊,構成一棵極小連通子圖,並使該連通子圖中n-1條邊上權值之和達到最小,則稱其為連通網的最小生成樹。

有兩種具體的實現演算法

①.kruskal演算法

②.prim演算法

兩者都用到了貪心演算法。

1:最小生成樹的形成

在每次迴圈之前,我們要保證集合a是某棵最小生成樹的乙個子集。

在每一步,我們要做的是選擇一條邊(u,v),將其加入到集合a中,使得a不違反迴圈不變式。我們稱這樣的邊為安全邊。

generic_mst(g, w)

a while a does not form a spanning tree

do find an edge(u, v) that is safe for a

a return a

2:克魯斯卡爾(kruskal)演算法

(1)演算法介紹

基本思想:按照權值從小到大的順序選擇n-1條邊,並保證這n-1條邊不構成迴路。 

具體做法:首先構造乙個只含n個頂點的森林,然後依權值從小到大從連通網中選擇邊加入到森林中,並使森林中不產生迴路,直至森林變成一棵樹為止。

(2)圖示

以上圖g4為例,來對克魯斯卡爾進行演示(假設,用陣列r儲存最小生成樹結果)。

第1步:將邊加入r中。

邊的權值最小,因此將它加入到最小生成樹結果r中。

第2步:將邊加入r中。

上一步操作之後,邊的權值最小,因此將它加入到最小生成樹結果r中。

第3步:將邊加入r中。

上一步操作之後,邊的權值最小,因此將它加入到最小生成樹結果r中。

第4步:將邊加入r中。

上一步操作之後,邊的權值最小,但會和已有的邊構成迴路;因此,跳過邊。同理,跳過邊。將邊加入到最小生成樹結果r中。

第5步:將邊加入r中。

上一步操作之後,邊的權值最小,因此將它加入到最小生成樹結果r中。

第6步:將邊加入r中。

上一步操作之後,邊的權值最小,但會和已有的邊構成迴路;因此,跳過邊。同理,跳過邊。將邊加入到最小生成樹結果r中。

此時,最小生成樹構造完成!它包括的邊依次是:

(3)演算法解析

根據前面介紹的克魯斯卡爾演算法的基本思想和做法,我們能夠了解到,克魯斯卡爾演算法重點需要解決的以下兩個問題:

問題一對圖的所有邊按照權值大小進行排序。

問題二將邊新增到最小生成樹中時,怎麼樣判斷是否形成了迴路。

問題一很好解決,採用排序演算法進行排序即可。

問題二,處理方式是:記錄頂點在"最小生成樹"中的終點,頂點的終點是"在最小生成樹中與它連通的最大頂點"(關於這一點,後面會通過給出說明)。然後每次需要將一條邊新增到最小生存樹時,判斷該邊的兩個頂點的終點是否重合,重合的話則會構成迴路。 以下圖來進行說明:

在將加入到最小生成樹r中之後,這幾條邊的頂點就都有了終點:

(01)c的終點是f。

(02)d的終點是f。

(03)e的終點是f。

(04)f的終點是f。

關於終點,就是將所有頂點按照從小到大的順序排列好之後;某個頂點的終點就是"與它連通的最大頂點"。 因此,接下來,雖然是權值最小的邊。但是c和e的重點都是f,即它們的終點相同,因此,將加入最小生成樹的話,會形成迴路。這就是判斷迴路的方式。

(4)基本定義

// 鄰接矩陣

typedef struct _graph

graph, *pgraph;

// 邊的結構體

typedef struct _edgedata

edata;

graph是鄰接矩陣對應的結構體。

vexs用於儲存頂點,vexnum是頂點數,edgnum是邊數;matrix則是用於儲存矩陣資訊的二維陣列。例如,matrix[i][j]=1,則表示"頂點i(即vexs[i])"和"頂點j(即vexs[j])"是鄰接點;matrix[i][j]=0,則表示它們不是鄰接點。

edata是鄰接矩陣邊對應的結構體。

(5)演算法實現

3:普里姆(prim)演算法

(1)演算法介紹

普里姆(prim)演算法,和克魯斯卡爾演算法一樣,是用來求加權連通圖的最小生成樹的演算法。 

基本思想 

對於圖g而言,v是所有頂點的集合;現在,設定兩個新的集合u和t,其中u用於存放g的最小生成樹中的頂點,t存放g的最小生成樹中的邊。 從所有uєu,vє(v-u) (v-u表示出去u的所有頂點)的邊中選取權值最小的邊(u, v),將頂點v加入集合u中,將邊(u, v)加入集合t中,如此不斷重複,直到u=v為止,最小生成樹構造完畢,這時集合t中包含了最小生成樹中的所有邊。

(2)圖示

以上圖g4為例,來對普里姆進行演示(從第乙個頂點a開始通過普里姆演算法生成最小生成樹)。

初始狀態:v是所有頂點的集合,即v=;u和t都是空!

第1步:將頂點a加入到u中。

此時,u=。

第2步:將頂點b加入到u中。

上一步操作之後,u=, v-u=;因此,邊(a,b)的權值最小。將頂點b新增到u中;此時,u=。

第3步:將頂點f加入到u中。

上一步操作之後,u=, v-u=;因此,邊(b,f)的權值最小。將頂點f新增到u中;此時,u=。

第4步:將頂點e加入到u中。

上一步操作之後,u=, v-u=;因此,邊(f,e)的權值最小。將頂點e新增到u中;此時,u=。

第5步:將頂點d加入到u中。

上一步操作之後,u=, v-u=;因此,邊(e,d)的權值最小。將頂點d新增到u中;此時,u=。

第6步:將頂點c加入到u中。

上一步操作之後,u=, v-u=;因此,邊(d,c)的權值最小。將頂點c新增到u中;此時,u=。

第7步:將頂點g加入到u中。

上一步操作之後,u=, v-u=;因此,邊(f,g)的權值最小。將頂點g新增到u中;此時,u=v。

此時,最小生成樹構造完成!它包括的頂點依次是:a b f e d c g

(3)基本定義

// 鄰接矩陣

typedef struct _graph

graph, *pgraph;

// 邊的結構體

typedef struct _edgedata

edata;

graph是鄰接矩陣對應的結構體。 

vexs用於儲存頂點,vexnum是頂點數,edgnum是邊數;matrix則是用於儲存矩陣資訊的二維陣列。例如,matrix[i][j]=1,則表示"頂點i(即vexs[i])"和"頂點j(即vexs[j])"是鄰接點;matrix[i][j]=0,則表示它們不是鄰接點。 

edata是鄰接矩陣邊對應的結構體。

(4)演算法實現

4:時間複雜度分析

對於克魯斯卡爾(kruskal)演算法:時間複雜度為o(elgv)

對於普里姆(prim)演算法:

其取決於最小優先佇列q的實現方式,時間複雜度與克魯斯卡爾(kruskal)演算法相同,o(elgv)。

如果使用斐波那契堆來實現最小優先佇列,時間複雜度為o(e+vlgv)。

最小生成樹(演算法導論第23章)

下面討論的兩種最小生成樹演算法都是貪心演算法。貪心演算法的每一步必須在多個可能的選擇中選擇一種,貪心演算法選擇在當前看來最好的選擇。通用演算法是每個時刻生長最小生成樹的一條邊 安全邊 並在整個策略的實施過程中,管理乙個遵守下述迴圈不變式的邊集合a 在每遍迴圈之前,a是某棵最小生成樹的子集。gener...

《演算法導論》第23章 最小生成樹

在上圖中,就是邊cd。這兩個定理看不懂沒關係,不影響後面學習kruskal演算法與prim演算法。kruskal演算法 我的理解 1 尋找權值最小的邊,如果邊的兩個頂點屬於不同的集合就加入到a 2 重複過程1,直到找到n 1條邊 第一點中分集合的方法我借鑑了wxdjss的部落格,以下是他的部落格鏈結...

演算法導論 最小生成樹

華電北風吹 日期 2016 1 16 常用的最小生成樹演算法有prime演算法和kruskal演算法。prime演算法基於節點,kruskal基於邊。1 prime演算法 prime演算法屬於貪心演算法,與廣度優先搜尋類似。保持乙個屬於已發現的最小生成樹節點的集合。每一次從未在樹中的節點中選擇乙個與...