C 資料結構與演算法揭秘14

2021-09-23 21:29:25 字數 4328 閱讀 5076

好久, 沒寫blog了,今天,多寫點。 

上節說到那裡了,是不是說圖的遍歷,這節,我們來通過圖的具體的應用了。

首先,看看最小生成樹的應用了。 什麼是最小生成樹了?

由生成樹的定義可知,無向連通圖的生成樹不是唯一的,對連通圖的不同遍歷就得到不同的生成樹。圖 b 所示是圖 (a)所示的無向連通圖的部分生成樹。

所謂最小生成樹是 如果是乙個無向連通網, 那麼它的所有生成樹中必有一棵邊的權值總和最小的生成樹,我們稱這棵生成樹為最小代價生成樹(minimum cost spanning tree).

許多應用問題都是乙個求無向連通網的最小生成樹問題。例如,要在 n個城市之間鋪設光纜, 鋪設光纜的費用很高, 並且各個城市之間鋪設光纜的費用不同。乙個目標是要使這 n個城市的任意兩個之間都可以直接或間接通訊, 另乙個目標是要使鋪設光纜的總費用最低。如果把 n個城市看作是圖的 n個頂點,兩個城市之間鋪設的光纜看作是兩個頂點之間的邊, 這實際上就是求乙個無向連通網的最小生成樹問題。

由最小生成樹的定義可知, 構造有 n個頂點的無向連通網的最小生成樹必須滿足以下三個條件: 

(1)構造的最小生成樹必須包括 n個頂點;

(2)構造的最小生成樹有且僅有 n-1條邊;

(3)構造的最小生成樹中不存在迴路。

構造最小生成樹的方法有許多種,典型的方法有兩種,一種是普里姆(prim)演算法,一種是克魯斯卡爾(kruskal)演算法。

一普里姆(prim)演算法

假設 g=(v,e)為一無向連通網,其中,v 為網中頂點的集合,e 為網中邊的集合。設定兩個新的集合 u 和 t,其中,u 為 g 的最小生成樹的頂點的集合,t 為 g 的最小生成樹的邊的集合。普里姆演算法的思想是:令集合 u 的初值為 u=(假設構造最小生成樹時從頂點 u1開始) ,集合 t 的初值為 t={}。從所有的頂點 u∈u 和頂點 v∈v-u 的帶權邊中選出具有最小權值的邊(u,v) ,將頂點 v 加入集合 u 中,將邊(u,v)加入集合 t 中。如此不斷地重複直到 u=v 時,最小生成樹構造完畢。此時,集合 u 中存放著最小生成樹的所有頂點,集合 t中存放著最小生成樹的所有邊。

以下圖(a)為例,說明用普里姆演算法構造圖的無向連通網的最小生成樹的過程。

為了分析問題的方便,無向連通網如圖(a)所示。 初始時, 演算法的集合 u=, 集合 v-u=, 集合 t={},如圖(b)所示。在所有 u 為集合 u 中頂點、v 為集合 v-u 中頂點的邊(u,v)中尋找具有最小權值的邊,尋找到的邊是(a,d),權值為 20,把頂點 b 加入到集合u 中, 把邊(a,d)加入到集合 t 中, 如圖 (c)所示。 在所有 u為集合 u 中頂點、v 為集合 v-u 中頂點的邊(u,v)中尋找具有最小權值的邊, 尋找到的邊是(d,e), 權值為 10,把頂點 e 加入到集合 u 中,把邊(d,e)加入到集合 t 中,如圖(d)所示。隨後依次從集合 v-u 中加入到集合 u 中的頂點為 b、c,依次加入到集合t中的邊為(a,b)(權值為 60) 、(e,c) (權值為 70) ,分別如圖 (e)、(f)所示。最後得到的圖(f)所示就是原無向連通網的最小生成樹。

本書以無向網的鄰接矩陣類 netadjmatrix來實現普里姆演算法。netadjmatrix類的成員欄位與無向圖鄰接矩陣類 graphadjmatrix的成員字段一樣,不同的是,當兩個頂點間有邊相連線時,matirx 陣列中相應元素的值是邊的權值,而不是 1

無向網鄰接矩陣類 netadjmatrix源**的實現如下所示:

public class netadjmatrix: igraph//繼承與圖形的介面  

//獲取索引為index的頂點的資訊 演算法的時間複雜度是o(1)

public nodegetnode(int index)

//設定索引為index的頂點的資訊 演算法的時間複雜度是o(1)

public void setnode(int index, nodev)

//邊的數目屬性 可讀可寫的屬性

public int numedges

set

}//獲取matrix[index1, index2]的值 演算法的時間複雜度是o(1)

public int getmatrix(int index1, int index2)

//設定matrix[index1, index2]的值 演算法的複雜度是o(1)

public void setmatrix(int index1, int index2, int v)

//獲取頂點的數目 演算法的時間的複雜度是o(1)

public int getnumofvertex()

//獲取邊的數目 演算法的時間的複雜度是o(1)

public int getnumofedge()

//v是否是無向網的頂點

//如果包含這個頂點 返回為真,否則返回為假。

//由於這是一層迴圈,演算法的複雜度是o(n)

public bool isnode(nodev)

} return false;

} //獲得頂點v在頂點陣列中的索引

// 如果相等,返回相應的索引。

//由於是一層迴圈,時間的複雜度是o(n)

public int getindex(nodev)

} return i;

} //在頂點v1、v2之間新增權值為v的邊

//新增相應的權值的v的邊, 這是乙個對稱矩陣。

public void setedge(nodev1, nodev2, int v)

//矩陣是對稱矩陣

matrix[getindex(v1), getindex(v2)] = v;

matrix[getindex(v2), getindex(v1)] = v;

++numedges;

} //刪除v1和v2之間的邊

// 刪除對稱矩陣。

public void deledge(nodev1, nodev2)

//v1和v2之間存在邊

if (matrix[getindex(v1), getindex(v2)] != int.maxvalue)

} //判斷v1和v2之間是否存在邊

//判斷相應 不是 最大值 返回為真 否則 為假 演算法的時間複雜度o(1)

public bool isedge(nodev1, nodev2)

//v1和v2之間存在邊

if (matrix[getindex(v1), getindex(v2)] != int.maxvalue)

else //v1和v2之間不存在邊 } }

具體如圖所示:

為實現普里姆演算法, 需要設定兩個輔助一維陣列 lowcost和 closevex, lowcost用來儲存集合 v-u 中各頂點與集合 u 中各頂點構成的邊中具有最小權值的邊的權值;closevex 用來儲存依附於該邊的在集合 u 中的頂點。假設初始狀態時,u=(u1為出發的頂點) ,這時有 lowcost[0]=0,它表示頂點 u1已加入集合 u中。陣列 lowcost 元素的值是頂點 u1 到其他頂點所構成的直接邊的權值。然後不斷選取權值最小的邊(ui,uk)(ui∈u,uk∈v-u),每選取一條邊,就將 lowcost[k]置為 0,表示頂點 uk 已加入集合 u 中。由於頂點 uk 從集合 v-u 進入集合 u 後,這兩個集合的內容發生了變化, 就需要依據具體情況更新陣列lowcost和closevex中部分元素的值。把普里姆演算法 primnetadjmatrix類的成員方法,實現的源**如下:

public int prim() 

//某個頂點加入集合u

lowcost[0] = 0;

closevex[0] = 0;

//判斷最小的權值通過的頂點的迴圈就此開始

for(int i=0; i具體實現,如圖所示:

在普里姆演算法中,第乙個for迴圈的執行次數為n-1,第二個for迴圈中又包括了乙個while迴圈和乙個for迴圈,執行次數為 2(n-1)2,所以普里姆演算法的時間複雜度為o(n2)。

這節,我們隊圖的引用做了乙個拋磚引玉的介紹,主要是普利姆演算法,解決 最小生成樹問題。下節,我們介紹一下克魯斯卡爾演算法解決最小生成樹的問題,和其他的應用。對圖做乙個總結等等。

C 資料結構與演算法揭秘14

好久,沒寫blog了,今天,多寫點。上節說到那裡了,是不是說圖的遍歷,這節,我們來通過圖的具體的應用了。首先,看看最小生成樹的應用了。什麼是最小生成樹了?由生成樹的定義可知,無向連通圖的生成樹不是唯一的,對連通圖的不同遍歷就得到不同的生成樹。圖 b 所示是圖 a 所示的無向連通圖的部分生成樹。所謂最...

資料結構與演算法 揭秘

字面意思就是研究資料的一種方法,就是研究資料在程式中組織的一種方法。資料結構就是,元素與元素有一種或者多種關係的集合,在軟體界有一種比較普片的公式就是程式 資料結構 演算法。1 集合 set 和數學的集合一樣,具有唯一性,確定性,無序性。2 線性結構 典型的資料庫二維表,一對一的關係。3 樹形結構 ...

C 資料結構與演算法揭秘一

這裡,我們 來說一說c 的資料結構了。什麼是資料結構。資料結構,字面意思就是研究資料的方法,就是研究資料如何在程式中組織的一種方法。資料結構就是相互之間存在一種或多種特定關係的資料元素的集合。程式界有一點很經典的話,程式設計 資料結構 演算法。用源 來體現,資料結構,就是程式設計。他有哪些具體的關係...