最小生成樹是經過無向連通帶權圖進行轉換生成的,在一開始,先了解下圖這個資料結構及相關概念。
圖可用二元組表示,g(v,e),其中v表示頂點的非空有限集合,e表示邊的有限集合。
以下圖g1則為有向圖,g2為無向圖,g1中每條邊都是有方向的,在有向圖中<1,2>、<2,1>是不同的兩個方向邊,無向圖則代表同一邊。
連通:在無向圖中,若從頂點vi到頂點vj之間有路徑,則這兩個頂點是連通的。
連通圖:如果圖中任意一對頂點是連通的則為連通圖。
連通分量,非連通圖的每乙個連通部分叫做連通分量,連通圖的連通分量就是其本身。
每條邊都有其在特定場景下的值,比如長度,比如粗細等,具體的例項有供水管網中的管道長度,網路建設中的阻抗等。
乙個連通圖g的子圖如果是乙個包含了g的所有頂點的樹,則該子圖為g的生成樹,有兩種演算法可以得到生成樹,一種是深度優先搜尋演算法,一種是廣度優先搜尋演算法,深度優先得到的生成樹為dfs生成樹,廣度優先為bfs生成樹。具體可以參考深度優先與廣度優先演算法
最小生成樹是生成樹的一種特例,『最小』指的是生成樹所有的邊加起來的權值最小,所以最小生成樹是在帶權圖中生成的。
演算法思想:建立並擴充套件一棵樹,為它新增新的樹枝
演算法核心:設u為最小生成樹的頂點集,v為圖g的頂點集合,目的是讓u中的頂點與v-u中的頂點結合找到一條最短邊來擴充生成樹t
演算法步驟說明:
下圖為我構造的乙個無向連通帶權圖g,有6個頂點,8條邊
vertexs = ['a', 'b', 'c', 'd', 'e', 'f']
edges = [('a', 'b', 2),('a', 'c', 1),('a', 'd', 5),('b', 'd', 4),('c', 'e', 3),('c', 'f', 4),('d', 'e', 2),('e', 'f', 7)] // 第三個數值為邊的權值
通過普里姆演算法思想,下圖說明了建立並擴充套件一棵樹的過程。
整個構建的過程為,初始所構建的最小生成樹為空,只有乙個頂點a,然後在已有圖g中的所有邊中進行查詢,查詢a頂點相關的邊,且此邊的權值最小,於是找到了('a','c',1),將('a','c',1)的邊與c頂點加入到最小生成樹,繼續往下找,現在最小生成樹中已經有了頂點a,c,邊('a','c',1),於是再進行一次迴圈,查詢圖g中所有邊中,與頂點a,c形成的邊的權值最小,經過比較,發現('a','b',2)滿足,於是將('a','b',2)與頂點b加入最小生成樹,然後繼續往下找,直到將圖g中的所有頂點都加入到最小生成樹中,則查詢結束。為此我們整個查詢過程就需要圖g頂點的個數減1
次的迴圈,因此初始頂點a已經加入最小生成樹中,於是只需要頂點個數減1次查詢次數即可。
以下是普里姆演算法的python**:
# 構建乙個樹
# 入選頂點集與入選邊集
tree_vertex = ['a']
tree_edges =
for i in range(len(vertex) - 1):
# 最小權值的邊
mini_weight = ()
for edge in edges:
# 如果邊中有兩個頂點在入選頂點集中,則此條邊需要忽略
if edge[0] in tree_vertex and edge[1] in tree_vertex:
continue
# 如果邊有乙個頂點在入選頂點集中,則此條邊需要進行比較權值
if edge[0] in tree_vertex or edge[1] in tree_vertex:
if not mini_weight:
mini_weight = edge
elif mini_weight[2] > edge[2]:
mini_weight = edge
# 將邊和點新增進最小生成樹
if mini_weight[0] not in tree_vertex:
if mini_weight[1] not in tree_vertex:
print(tree_edges) # [('a', 'c', 1), ('a', 'b', 2), ('c', 'e', 3), ('d', 'e', 2), ('c', 'f', 4)]
print(tree_vertex) # ['a', 'c', 'b', 'e', 'd', 'f']
簡化查詢,將邊按照權值排序for i in range(0, len(vertex) - 1):
for edge in edges:
# 形成迴路,拋棄這條邊
if edge[0] in tree_vertex and edge[1] in tree_vertex:
continue
# 邊中包含入選頂點,並且此條邊為最小權值
if edge[0] in tree_vertex or edge[1] in tree_vertex:
if edge[0] in tree_vertex:
elif edge[1] in tree_vertex:
break
print(tree_edges) # [('a', 'c', 1), ('a', 'b', 2), ('c', 'e', 3), ('d', 'e', 2), ('c', 'f', 4)]
print(tree_vertex) # ['a', 'c', 'b', 'e', 'd', 'f']
演算法思想:擴充套件一棵樹的集,並構成一棵生成樹
演算法核心:設u為最小生成樹的頂點集,v為圖g的頂點集合,目的是讓u中的頂點與v-u中的頂點結合找到一條最短邊來擴充生成樹t
演算法步驟說明:
繼續使用上圖的無向連通帶權圖g來進行說明:
整個構建的過程為,初始所構建的最小生成樹包含了圖g的所有頂點,選中某一頂點(假設為a),然後在已有圖g中的所有邊(經過了按權值排序)中進行查詢,查詢a頂點相關的邊,且此邊的不構成環路,於是找到了('a','c',1),將('a','c',1)的邊加入到最小生成樹,繼續往下找,再進行一次迴圈,找到('a','b',2),發現('a','b',2)與最小生成樹中的邊,並未構成環路,於是加入,繼續往下找,直到最小生成樹中的所有頂點都在乙個連通分量中,則查詢結束。
以下是克魯斯卡爾演算法的python**:
vertexs = ['a', 'b', 'c', 'd', 'e', 'f']
edges = [('a', 'c', 1), ('a', 'b', 2), ('d', 'e', 2), ('c', 'e', 3), ('b', 'd', 4), ('c', 'f', 4), ('a', 'd', 5),
('e', 'f', 7)]
# 最小生成樹
tree_edges =
# 頂點的標記,初始頂點的標記都不相同
vertexs_sign =
for edge in edges:
# 判斷edge與最小生成樹中的邊是否構成環
if vertexs_sign[edge[0]] == vertexs_sign[edge[1]]:
# 形成了環,忽略此條邊
continue
if vertexs_sign[edge[0]] != vertexs_sign[edge[1]]:
# 將加入的邊的頂點的相同標記的頂點都設定為一樣
temp = vertexs_sign[edge[0]]
for key in vertexs_sign:
if vertexs_sign[key] == temp:
vertexs_sign[key] = vertexs_sign[edge[1]]
print(vertexs_sign)
if len(tree_edges) == len(vertexs) - 1:
break
print(tree_edges) # [('a', 'c', 1), ('a', 'b', 2), ('d', 'e', 2), ('c', 'e', 3), ('c', 'f', 4)]
print(vertexs_sign) #
1、圖是一種相較於樹的資料結構,圖分為有向圖與無向圖,分為連通圖與非連通圖,分為帶權圖與不帶權圖2、圖g的乙個包含所有頂點的連通子圖則為圖g的生成樹,帶權圖的權值加起來最小則為最小生成樹
3、最小生成樹有兩種演算法,prim(普利姆)演算法與kruskal(克魯斯卡爾)演算法,為了更加方便實現演算法,將圖的邊集進行按權值排序,可以更快生成最小生成樹
最小生成樹之普里姆演算法
什麼是最小生成樹?定義 給定乙個帶權無向連通圖,從指定結點出發能夠連線所有結點並且權值總和最小的樹叫最小生成樹,也叫權重最小樹。那麼普里姆演算法是怎麼構造最小生成樹的?首先將指定的結點加到到頂點集合v中,由集合內和集合外構成的所有邊當中選取權值最小的邊,將其對應的集合外的點加入到集合中,重複此操作,...
最小生成樹(普里姆演算法)
關於什麼是prim 普里姆演算法 在實際生活中,我們常常碰到類似這種一類問題 如果要在n個城市之間建立通訊聯絡網,則連通n個城市僅僅須要n 1條線路。這時。我們須要考慮這樣乙個問題。怎樣在最節省經費前提 下建立這個通訊網.換句話說,我們須要在這n個城市中找出乙個包括全部城市的連通子圖,使得 其全部邊...
最小生成樹 普里姆演算法
普利姆演算法 最小生成樹 把所有頂點分為 2 個集合 乙個表示已經選中的頂點集合 另乙個表示未選中的頂點集合 例如 a,b,c,d,e 五個頂點 1.任意選擇乙個頂點 放在 已經選中的頂點集合中 假如 選a 2.將a 與未選中頂點集合中 選擇 一頂點 條件 權值最小的乙個頂點 如何權值相同 則任意選...