圖的最小生成樹演算法
『壹』 圖所示是一個無向帶權圖,請分別按Prim演算法和Kruskal演算法求最小生成樹.
•普里姆(Prim)演算法
基本思想
假設N=(V,E)是一個具有n個頂點的連通網,T=(U,TE)是所求的最小生成樹,其中U是T的頂點集,TE是T的邊集。
(1)初始U={u0}(u0∈V),TE=φ;
(2)在所有u∈U,v∈V-U的邊中選一條代價最小的邊(u0,v0)並入集合TE,同時將v0並入U;
(3)重復(2),直到U=V為止。
此時,TE中必含有n-1條邊,則T=(V,{TE})為N的最小生成樹。
注意:1.最小生成樹不唯一。
2.該圖從節點最小開始。
『貳』 克魯斯卡爾演算法求最小生成樹
克魯斯卡爾演算法的基本思想,這是我自己結合教材理解的,難免有誤,謹慎參考:
1:將圖中的n頂點看成是n個集合。解釋為,圖中共有6個頂點,那麼就有六個集合。即a,b,c,d,e,f各自分別都是一個集合。{a},{b}等。
2:按權值由小到大的順序選擇邊。所選邊應滿足兩個頂點不在同一個頂點集合內。將該邊放到生成樹邊的集合,同時將該邊的兩個頂點所在的集合合並。這是書上的描述,可能有點難理解,這里解釋一下:
首先,選擇權值最小的邊,即為圖中的(a,c)邊,此時a,c滿足不在同一個頂點集合內,將這個邊記錄下來,然後合並這兩個頂點的集合,即此時剩下五個頂點集合了,{a,c},{b},{d},{e},{f}
3:重復步驟2,直到所有的頂點都在同一個集合內!解釋如下:
此時剩下的邊中權值最小的為(d,f),滿足不在同一個頂點集合,所以記錄下該邊,然後合並這兩個頂點集合。新的頂點集合為{a,c} {b} {e} {d,f}
接著,繼續重復,選擇邊(b,e),滿足不在同一個頂點集合內,所以記錄下該邊,然後再次合並這兩個集合,新的集合為{a,c} {d,f} {b,e}
繼續,選擇邊(c,f),滿足不在同一個頂點集合內,所以記錄下該邊,然後合並這兩個頂點所在的集合,新集合為{a,c,d,f} {b,e}
再繼續,選擇權值為15的邊,發現邊(c,d)和邊(a,d)都不滿足條件不在同一個頂點集合內,所以只能選擇邊(b,c),記錄下該邊,然後合並頂點集合,新集合為{a,b,c,d,e,f},此時所有點都在同一集合內,所以結束!
4:將上面我們記錄的那些邊連接起來就行了!這就是最小生成樹,附本人手繪:
『叄』 怎麼求一幅圖像的最小生成樹
兩種演算法:
舉例說明:給出下圖計算其最小生成樹。
『肆』 C語言(關於圖最小生成樹方法)
/*
鄰接矩陣存儲圖
測試數據
610
126
131
145
235
253
345
356
364
462
566
*/
#include<stdio.h>
#include<limits.h>
#defineN100
intp[N],key[N],tb[N][N];
voidprim(intv,intn)
{
inti,j;
intmin;
for(i=1;i<=n;i++)
{
p[i]=v;
key[i]=tb[v][i];
}
key[v]=0;
for(i=2;i<=n;i++)
{
min=INT_MAX;
for(j=1;j<=n;j++)
if(key[j]>0&&key[j]<min)
{
v=j;
min=key[j];
}
printf("%d%d",p[v],v);
key[v]=0;
for(j=1;j<=n;j++)
if(tb[v][j]<key[j])
p[j]=v,key[j]=tb[v][j];
}
}
intmain()
{
intn,m;
inti,j;
intu,v,w;
while(scanf("%d%d",&n,&m))
{
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
tb[i][j]=INT_MAX;
}
while(m--)
{
scanf("%d%d%d",&u,&v,&w);
tb[u][v]=tb[v][u]=w;
}
prim(1,n);
printf(" ");
}
return0;
}
『伍』 最小生成樹怎麼求
一個有 n 個結點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的所有 n 個結點,並且有保持圖連通的最少的邊。最小生成樹可以用kruskal(克魯斯卡爾)演算法或Prim(普里姆)演算法求出。
求MST的一般演算法可描述為:針對圖G,從空樹T開始,往集合T中逐條選擇並加入n-1條安全邊(u,v),最終生成一棵含n-1條邊的MST。
當一條邊(u,v)加入T時,必須保證T∪{(u,v)}仍是MST的子集,我們將這樣的邊稱為T的安全邊。
偽代碼
GenerieMST(G){//求G的某棵MST
T〈-¢; //T初始為空,是指頂點集和邊集均空
while T未形成G的生成樹 do{
找出T的一條安全邊(u,v);//即T∪{(u,v)}仍為MST的子集
T=T∪{(u,v)}; //加入安全邊,擴充T
}
return T; //T為生成樹且是G的一棵MST
}
注意:
下面給出的兩種求MST的演算法均是對上述的一般演算法的求精,兩演算法的區別僅在於求安全邊的方法不同。
為簡單起見,下面用序號0,1,…,n-1來表示頂點集,即是:
V(G)={0,1,…,n-1},
G中邊上的權解釋為長度,並設T=(U,TE)。
求最小生成樹的具體演算法(pascal):
Prim演算法
procere prim(v0:integer);
var
lowcost,closest:array[1..maxn] of integer;
i,j,k,min:integer;
begin
for i:=1 to n do begin
lowcost[i]:=cost[v0,i];
closest[i]:=v0;
end;
for i:=1 to n-1 do begin
{尋找離生成樹最近的未加入頂點 k}
min:=maxlongint;
for j:=1 to n do
if (lowcost[j]<min) and (lowcost[j]<>0) then begin
min:=lowcost[j];
k:=j;
end;
lowcost[k]:=0; {將頂點k 加入生成樹}
{生成樹中增加一條新的邊 k 到 closest[k]}
{修正各點的 lowcost 和 closest 值}
for j:=1 to n do
if cost[k,j]<lowcost[j] then begin
lowcost[j]:=cost[k,j];
closest[j]:=k;
end;
end;
end;
Kruskal演算法
按權值遞增順序刪去圖中的邊,若不形成迴路則將此邊加入最小生成樹。
function find(v:integer):integer; {返回頂點 v 所在的集合}
var i:integer;
begin
i:=1;
while (i<=n) and (not v in vset) do inc(i);
if i<=n then find:=i else find:=0;
end;
procere kruskal;
var
tot,i,j:integer;
begin
for i:=1 to n do vset:=i;{初始化定義 n 個集合,第 I個集合包含一個元素 I}
p:=n-1; q:=1; tot:=0; {p 為尚待加入的邊數,q 為邊集指針}
sort;
{對所有邊按權值遞增排序,存於 e中,e.v1 與 e.v2 為邊 I 所連接的兩個頂點的
序號,e.len 為第 I條邊的長度}
while p>0 do begin
i:=find(e[q].v1);j:=find(e[q].v2);
if i<>j then begin
inc(tot,e[q].len);
vset:=vset+vset[j];vset[j]:=[];
dec(p);
end;
inc(q);
end;
writeln(tot);
end;
C語言代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include<stdio.h>
#include<stdlib.h>
#include<iostream.h>
#defineMAX_VERTEX_NUM20
#defineOK1
#defineERROR0
#defineMAX1000
typedefstructArcell
{
doubleadj;
}Arcell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedefstruct
{
charvexs[MAX_VERTEX_NUM];//節點數組
AdjMatrixarcs;//鄰接矩陣
intvexnum,arcnum;//圖的當前節點數和弧數
}MGraph;
typedefstructPnode//用於普利姆演算法
{
charadjvex;//節點
doublelowcost;//權值
}Pnode,Closedge[MAX_VERTEX_NUM];//記錄頂點集U到V-U的代價最小的邊的輔助數組定義
typedefstructKnode//用於克魯斯卡爾演算法中存儲一條邊及其對應的2個節點
{
charch1;//節點1
charch2;//節點2
doublevalue;//權值
}Knode,Dgevalue[MAX_VERTEX_NUM];
//-------------------------------------------------------------------------------
intCreateUDG(MGraph&G,Dgevalue&dgevalue);
intLocateVex(MGraphG,charch);
intMinimum(MGraphG,Closedgeclosedge);
voidMiniSpanTree_PRIM(MGraphG,charu);
voidSortdge(Dgevalue&dgevalue,MGraphG);
//-------------------------------------------------------------------------------
intCreateUDG(MGraph&G,Dgevalue&dgevalue)//構造無向加權圖的鄰接矩陣
{
inti,j,k;
cout<<"請輸入圖中節點個數和邊/弧的條數:";
cin>>G.vexnum>>G.arcnum;
cout<<"請輸入節點:";
for(i=0;i<G.vexnum;++i)
cin>>G.vexs[i];
for(i=0;i<G.vexnum;++i)//初始化數組
{
for(j=0;j<G.vexnum;++j)
{
G.arcs[i][j].adj=MAX;
}
}
cout<<"請輸入一條邊依附的定點及邊的權值:"<<endl;
for(k=0;k<G.arcnum;++k)
{
cin>>dgevalue[k].ch1>>dgevalue[k].ch2>>dgevalue[k].value;
i=LocateVex(G,dgevalue[k].ch1);
j=LocateVex(G,dgevalue[k].ch2);
G.arcs[i][j].adj=dgevalue[k].value;
G.arcs[j][i].adj=G.arcs[i][j].adj;
}
returnOK;
}
intLocateVex(MGraphG,charch)//確定節點ch在圖G.vexs中的位置
{
inta;
for(inti=0;i<G.vexnum;i++)
{
if(G.vexs[i]==ch)
a=i;
}
returna;
}
voidMiniSpanTree_PRIM(MGraphG,charu)//普利姆演算法求最小生成樹
{
inti,j,k;
Closedgeclosedge;
k=LocateVex(G,u);
for(j=0;j<G.vexnum;j++)
{
if(j!=k)
{
closedge[j].adjvex=u;
closedge[j].lowcost=G.arcs[k][j].adj;
}
}
closedge[k].lowcost=0;
for(i=1;i<G.vexnum;i++)
{
k=Minimum(G,closedge);
cout<<"("<<closedge[k].adjvex<<","<<G.vexs[k]<<","<<closedge[k].lowcost<<")"<<endl;
closedge[k].lowcost=0;
for(j=0;j<G.vexnum;++j)
{
if(G.arcs[k][j].adj<closedge[j].lowcost)
{
closedge[j].adjvex=G.vexs[k];
closedge[j].lowcost=G.arcs[k][j].adj;
}
}
}
}
intMinimum(MGraphG,Closedgeclosedge)//求closedge中權值最小的邊,並返回其頂點在vexs中的位置
{
inti,j;
doublek=1000;
for(i=0;i<G.vexnum;i++)
{
if(closedge[i].lowcost!=0&&closedge[i].lowcost<k)
{
k=closedge[i].lowcost;
j=i;
}
}
returnj;
}
voidMiniSpanTree_KRSL(MGraphG,Dgevalue&dgevalue)//克魯斯卡爾演算法求最小生成樹
{
intp1,p2,i,j;
intbj[MAX_VERTEX_NUM];//標記數組
for(i=0;i<G.vexnum;i++)//標記數組初始化
bj[i]=i;
Sortdge(dgevalue,G);//將所有權值按從小到大排序
for(i=0;i<G.arcnum;i++)
{
p1=bj[LocateVex(G,dgevalue[i].ch1)];
p2=bj[LocateVex(G,dgevalue[i].ch2)];
if(p1!=p2)
{
cout<<"("<<dgevalue[i].ch1<<","<<dgevalue[i].ch2<<","<<dgevalue[i].value<<")"<<endl;
for(j=0;j<G.vexnum;j++)
{
if(bj[j]==p2)
bj[j]=p1;
}
}
}
}
voidSortdge(Dgevalue&dgevalue,MGraphG)//對dgevalue中各元素按權值按從小到大排序
{
inti,j;
doubletemp;
charch1,ch2;
for(i=0;i<G.arcnum;i++)
{
for(j=i;j<G.arcnum;j++)
{
if(dgevalue[i].value>dgevalue[j].value)
{
temp=dgevalue[i].value;
dgevalue[i].value=dgevalue[j].value;
dgevalue[j].value=temp;
ch1=dgevalue[i].ch1;
dgevalue[i].ch1=dgevalue[j].ch1;
dgevalue[j].ch1=ch1;
ch2=dgevalue[i].ch2;
dgevalue[i].ch2=dgevalue[j].ch2;
dgevalue[j].ch2=ch2;
}
}
}
}
voidmain()
{
inti,j;
MGraphG;
charu;
Dgevaluedgevalue;
CreateUDG(G,dgevalue);
cout<<"圖的鄰接矩陣為:"<<endl;
for(i=0;i<G.vexnum;i++)
{
for(j=0;j<G.vexnum;j++)
cout<<G.arcs[i][j].adj<<"";
cout<<endl;
}
cout<<"=============普利姆演算法===============\n";
cout<<"請輸入起始點:";
cin>>u;
cout<<"構成最小代價生成樹的邊集為:\n";
MiniSpanTree_PRIM(G,u);
cout<<"============克魯斯科爾演算法=============\n";
cout<<"構成最小代價生成樹的邊集為:\n";
MiniSpanTree_KRSL(G,dgevalue);
}
pascal演算法
program didi;
var
a:array[0..100000] of record
s,t,len:longint;
end;
fa,r:array[0..10000] of longint;
n,i,j,x,y,z:longint;
tot,ans:longint;
count,xx:longint;
procere quick(l,r:longint);
var
i,j,x,y,t:longint;
begin
i:=l;j:=r;
x:=a[(l+r) div 2].len;
repeat
while x>a[i].len do inc(i);
while x<a[j].len do dec(j);
if i<=j then
begin
y:=a[i].len;a[i].len:=a[j].len;a[j].len:=y;
y:=a[i].s;a[i].s:=a[j].s;a[j].s:=y;
y:=a[i].t;a[i].t:=a[j].t;a[j].t:=y;
inc(i);dec(j);
end;
until i>j;
if i<r then quick(i,r);
if l<j then quick(l,j);
end;
function find(x:longint):longint;
begin
if fa[x]<>x then fa[x]:=find(fa[x]);
find:=fa[x];
end;
procere union(x,y:longint);{啟發式合並}
var
t:longint;
begin
x:=find(x);
y:=find(y);
if r[x]>r[y] then
begin
t:=x;x:=y;y:=t;
end;
if r[x]=r[y] then inc(r[x]);
fa[x]:=y;
end;
begin
readln(xx,n);
for i:=1 to xx do fa[i]:=i;
for i:=1 to n do
begin
read(x,y,z);
inc(tot);
a[tot].s:=x;
a[tot].t:=y;
a[tot].len:=z;
end;
quick(1,tot);{將邊排序}
ans:=0;
count:=0;
i:=0;
while count<=x-1 do{count記錄加邊的總數}
begin
inc(i);
with a[i] do
if find(s)<find(t) then
begin
union(s,t);
ans:=ans+len;
inc(count);
end;
end;
write(ans);
end.
Prim
var
m,n:set of 1..100;
s,t,min,x,y,i,j,k,l,sum,p,ii:longint;
a:array[1..100,1..100]of longint;
begin
readln(p);
for ii:=1 to p do
begin
k:=0; sum:=0;
fillchar(a,sizeof(a),255);
readln(x);
m:=[1];
n:=[2..x];
for i:=1 to x do
begin
for j:=1 to x do
begin
read(a[i,j]);
if a[i,j]=0
then a[i,j]:=maxlongint;
end;
readln;
end;
for l:=1 to x-1 do
begin
min:=maxlongint;
for i:=1 to x do
if i in m
then begin
for j:=1 to x do
begin
if (a[i,j]<min)and(j in n)
then begin
min:=a[i,j];
s:=i;
t:=j;
end;
end;
end;
sum:=sum+min;
m:=m+[t];
n:=n-[t];
inc(k);
end;
writeln(sum);
end;
end.
『陸』 怎麼求最小生成樹 (離散數學 圖論)
1) 樹是無迴路的連通圖。
2)對於某個圖,求它的最小生成樹,比較簡單的方法,先畫出圖中所有節點,從權值最小的邊開始依次連接頂點,注意不要形成迴路,最後得到的圖就是最小生成樹。
『柒』 圖的最小生成樹演算法
圖的生成樹和最小生成樹生成樹(SpanningTree):如果一個圖的子圖是一個包含圖所有節點的樹,那這個子圖就稱為生成樹.
『捌』 求圖的生成樹的演算法有哪些
在圖論中,對於這種權值固定且為正的連通圖來說,有比較成熟的最小生成樹演算法。如著名的Prim演算法和Kruskal演算法,這兩個演算法都是貪心演算法的例子Prim演算法的時間復雜度為 ,適合求邊稠密的網路圖的最小生成樹;Kruskal演算法的時間復雜度為 ,適合求邊稀疏的網路圖的最小生成樹。
『玖』 數據結構(十):最小生成樹
最小生成樹是帶權無向連通圖中權值最小的生成樹,根據 圖 中生成樹定義可知, 個頂點的連通圖中,生成樹中邊的個數為 ,向生成樹中添加任意一條邊,則會形成環。生成樹存在多種,其中權值之和最小的生成樹即為最小生成樹。
若 為最小生成樹 的一個真子集,即 的頂點集合和邊集合都是 的頂點和邊集合的子集,構造最小生成樹過程為向 中添加頂點和邊,添加的原則有兩種:
kruskal 演算法即為上述第一種原則,通過選擇圖中的最小權值邊來構造最小生成樹,過程中需要注意避免形成環。
step 1:
最小權值邊為頂點 7、8 形成的邊
step 2:
最小權值邊為頂點 3、9 形成的邊
step 3:
最小權值邊為頂點 6、7 形成的邊
step 4:
最小權值邊為頂點 3、6 形成的邊
step 5:
最小權值邊為頂點 1、2 形成的邊
step 6:
最小權值邊為頂點 3、4 形成的邊
step 7:
最小權值邊為頂點 1、8 形成的邊
step 8:
最小權值邊為頂點 4、5 形成的邊
最小生成樹的權值之和為 37
這里使用鄰接表作為圖的存儲結構
這里使用 getEdgesFromAdjacencyList 函數完成鄰接表到邊集合的轉換,使用快排 sort 完成對邊集合的排序,使用 origin 函數返回每個子圖的根。
該函數返回頂點 index 所屬子圖的根頂點,其中 vertices[index] 位置上存儲的是頂點 index 的上一個頂點,每個子圖中,根頂點的上一個頂點為自身。
kruskal 演算法中使用 getEdgesFromAdjacencyList 函數完成鄰接表向邊集合的轉換,函數內部存在兩層循環,訪問鄰接表中每個頂點的相鄰頂點,復雜度為 。使用快排對邊集合進行排序,時間復雜度為 ,因為 ,所以快排時間復雜度可以表述為 。 kruskal 演算法中 while 循環取最小權值邊,並對邊的兩個頂點執行 origin 函數判斷是否屬於同一個子圖,時間復雜度為 。所以 kruskal 演算法的時間復雜度為 。
kruskal 演算法的過程為不斷對子圖進行合並,直到形成最終的最小生成樹。 prim 演算法的過程則是只存在一個子圖,不斷選擇頂點加入到該子圖中,即通過對子圖進行擴張,直到形成最終的最小生成樹。
step 1:
距離子圖的最近頂點為 4
step 2:
距離子圖的最近頂點為 3
step 3:
距離子圖的最近頂點為 9
step 4:
距離子圖的最近頂點為 6
step 5:
距離子圖的最近頂點為 7
step 6:
距離子圖的最近頂點為 8
step 7:
距離子圖的最近頂點為 2
step 8:
距離子圖的最近頂點為 1
最小生成樹的權值之和為 37
這里使用鄰接表作為圖的存儲結構
這里使用 vertices 列表存儲每個頂點元素,每個元素包括兩個屬性, index 為頂點下標, weight 為頂點距離子圖的大小。演算法中使用 verticesIndex 列表存儲每個頂點元素在 vertices 列表中的下標位置。使用 heapSort 堆排序對每個頂點到子圖的距離進行排序,即對 vertices 列表進行排序,使用堆排序內的 transformToHeap 函數調整 vertices 列表為小頂堆。當添加新頂點到子圖後,使用 updateVertices 函數完成對相鄰頂點的距離更新。
當 vertices 列表調整為小頂堆之後,將列表首、尾元素交換,則列表尾元素即為距離子圖最近的頂點元素。
對每一個相鄰頂點,如果不在子圖中,則判斷是否更新到子圖的距離。更新距離後的 while 循環操作,目的為調整堆結構為小頂堆。
prim 演算法中構造頂點列表的時間復雜度為 。使用堆排序對頂點列表進行排序,時間復雜度為 。 prim 演算法中 while 循環取最近頂點元素,並調整元素取出後列表的堆結構,所以調整復雜度為 ;同時,循環結構內執行 updateVertices 函數,更新每個取出頂點的相鄰頂點距離值,所以更新頂點數為 ,因為每個頂點更新距離後,需要調整堆結構為小頂堆,所以更新復雜度為 。所以 prim 演算法的總時間復雜度為 。
『拾』 最小生成樹的兩種演算法
主要有兩個:
1.普里姆(Prim)演算法
特點:時間復雜度為O(n2).適合於求邊稠密的最小生成樹。
2.克魯斯卡爾(Kruskal)演算法
特點:時間復雜度為O(eloge)(e為網中邊數),適合於求稀疏的網的最小生成樹。