最小生成樹

最小生成樹

最小生成樹其實是最小權重生成樹的簡稱。一個有n個結點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的所有n個結點,並且有保持圖連通的最少的邊。最小生成樹可以用kruskal(克魯斯卡爾)算法或Prim(普里姆)算法求出。最小生成樹性質:設G=(V,E)是一個連通網路,U是頂點集V的一個非空真子集。根據樹的定義,則T中必有一條從紅點u到藍點v的路徑P,且P上必有一條紫邊(u',v')連線紅點集和藍點集,否則u和v不連通。當一條邊(u,v)加入T時,必須保證T∪(u,v)仍是MST的子集,我們將這樣的邊稱為T的安全邊。

套用

最小生成樹最小生成樹

生成樹和最小生成樹有許多重要的套用。

例如:要在n個城市之間鋪設光纜,主要目標是要使這 n 個城市的任意兩個之間都可以通信,但鋪設光纜的費用很高,且各個城市之間鋪設光纜的費用不同,因此另一個目標是要使鋪設光纜的總費用最低。這就需要找到帶權的最小生成樹。

性質

說明

最小生成樹性質:設G=(V,E)是一個連通網路,U是頂點集V的一個非空真子集。若(u,v)是G中一條“一個端點在U中(例如:u∈U),另一個端點不在U中的邊(例如:v∈V-U),且(u,v)具有最小權值,則一定存在G的一棵最小生成樹包括此邊(u,v)。

證明

為方便說明,先作以下約定:

①將集合U中的頂點看作是紅色頂點,②而V-U中的頂點看作是藍色頂點,③連線紅點和藍點的邊看作是紫色邊,④權最小的紫邊稱為輕邊(即權重最"輕"的邊)。於是,MST性質中所述的邊(u,v)就可簡稱為輕邊。

用反證法證明MST性質:

假設G中任何一棵MST都不含輕邊(u,v)。則若T為G的任意一棵MST,那么它不含此輕邊。

根據樹的定義,則T中必有一條從紅點u到藍點v的路徑P,且P上必有一條紫邊(u',v')連線紅點集和藍點集,否則u和v不連通。當把輕邊(u,v)加入樹T時,該輕邊和P必構成了一個迴路。刪去紫邊(u',v')後迴路亦消除,由此可得另一生成樹T'。

T'和T的差別僅在於T'用輕邊(u,v)取代了T中權重可能更大的紫邊(u',v')。因為w(u,v)≤w(u',v'),所以

w(T')=w(T)+w(u,v)-w(u',v')≤w(T)

即T'是一棵比T更優的MST,所以T不是G的MST,這與假設矛盾。

所以,MST性質成立。

算法描述

求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算法

procedure 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]<>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]:=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;

procedure 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

186

#include<stdio.h>

#include<stdlib.h>

#include<iostream.h>

#defineMAX_VERTEX_NUM20

#defineOK1

#defineERROR0

#defineMAX1000

typedefstructArcell

{

double adj ;

}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

char ch2 ;//節點2

double value ;//權值

}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 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;

double temp ;

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算法

kruskal

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;

procedure 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 xdec(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

if l

end;

function find(x:longint):longint;

begin

if fa[x]<>x then fa[x]:=find(fa[x]);

find:=fa[x];

end;

procedure 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)

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:=;

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]

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.

相關搜尋

熱門詞條

聯絡我們