转自:https://www.cnblogs.com/fanmu/p/6082152.html
最小生成树
1.定义
2.kruskal 算法
3.Prim 算法
1.定义
G=(V,E)为连通无向图,V为结点的集合,E为结点的可能连接边
对每条边(u ,v)都赋予权重w(u ,v)
目标:找到一个无环子集T, 既能将所有结点连接起来,又具有最小权重。
T是由G生成的树,并把这种问题叫做最小生成树问题。
2.kruskal算法
主要思想:
将V的每个结点定义为一棵树,并定义根节点(代表)为该节点,将E中的边按权重从小到大依次处理。
首先判断边的两个结点是否属于同一棵树(根据根节点是否一致),若不是,则合并两棵树,并更新根节点;若是,则不予理会。
(这里是为了形成无环集合,保证权重和最小
如下图f所示,结点i、g已合并为一棵树,根节点一致,所以ig边不再纳入集合)
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
|
global
G
G.VV=
char
(97:105);
G.V=
cell
(9,1);
G.Adj={
'ab'
;
'ah'
;
'bc'
;
'bh'
;
'hi'
;
'hg'
;
'ig'
;
'gf'
;
'cf'
;
'cd'
;
'df'
;
'de'
;
'ef'
;
'ic'
};
G.Bdj=[4;8;8;11;7;1;6;2;4;7;14;9;10;2];
A=[];
for
i
=1:
length
(G.VV)
%MAKE-SET
G.V{
i
}.p=G.VV(
i
);
G.V{
i
}.
rank
=0;
end
[wei,index]=
sort
(G.Bdj);
la=G.Adj(index);
for
i
=1:
length
(la)
x=la{
i
};
a1=
find
(G.VV==x(1));
a2=
find
(G.VV==x(2));
if
find_set(a1)~=find_set(a2)
A=[A;x];
union
(a1,a2);
end
end
function
k= find_set(
i
)
%找到集合的代表,也就是根节点
global
G
if
G.V{
i
}.p~=G.VV(
i
)
%这里的G.V{i}.p是G.VV(i)所在子树的根节点
%函数的目标是找到合并之后的树(集合)的的结点
j
=
find
(G.VV==G.V{
i
}.p);
G.V{
i
}.p=find_set(
j
);
%不断更新,直到找到集合的根节点
end
k=G.V{
i
}.p;
end
function
union
(
i
,a)
%合并两个集合,并更新集合的根节点
%更新的原则是看子树的结点数目,多的那个的子树的代表当根节点
%注意,这里并没有更新子树的根节点,这一步骤是在find_set里完成的
global
G
x=find_set(
i
);
y=find_set(a);
aa=
find
(G.VV==x);
bb=
find
(G.VV==y);
if
G.V{aa}.
rank
>G.V{bb}.
rank
G.V{bb}.p=G.VV(aa);
else
G.V{aa}.p=G.VV(bb);
if
G.V{aa}.
rank
==G.V{bb}.
rank
G.V{bb}.
rank
=G.V{bb}.
rank
+1;
end
end
end
|
运行结果:
A =
hg
gf
ic
ab
cf
cd
ah
de
d=37
3.prim 算法
关于轻量级边的定义:
主要思想:
给定连通图G和任意根节点r,最小生成树从结点r开始,一直长大到覆盖V中所有结点为止,即不断寻找轻量级边以实现最小权重和
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
|
%最小生成树-prim算法
G.VV=
char
(97:105);
G.Adj={
'bh'
;
'ahc'
;
'bifd'
;
'cfe'
;
'df'
;
'cdeg'
;
'ihf'
;
'abig'
;
'cgh'
};
%邻接链表
G.Bdj={[4 8];[4 11 8];[8 2 4 7];[7 14 9];[9 10];[4 14 10 2];[6 1 2];[8 11 7 1];[2 6 7]};
%邻接链表对应权重
Q=G.VV;
Q(Q==G.VV(1))=[];
r=1;
x(r)=G.VV(1);
%给定初始点
d=0;
while
length
(Q)~=0
[wei,index]=
sort
(unionwei(G,x,r));
%!!!关键点,目的是横跨(V-Q,Q)的轻量级边的一个端点,即权重最小的一个点
u=unionla(G,x,r);
u=u(index);
for
i
=1:
length
(u)
if
find
(Q==u(
i
))
k=
i
;
break
;
end
end
d=d+wei(k);
r=r+1;
x(r)=u(k);
Q(Q==u(k))=[];
%找到后Q中删除,以保证每个点只被访问一次
end
fprintf
(
'path:'
);x
fprint(
'\n'
);
fprintf
(
'd= %d \n'
,d);
function
wei0=unionwei( G,x,r )
%合并权重向量,方便排序
wei0=[];
for
i
=1:r
a=
find
(G.VV==x(
i
));
wei1=G.Bdj{a};
wei0=[wei0 wei1];
end
end
function
la0 = unionla(G,x, r )
%合并权重对应的边
la0=[];
for
i
=1:r
a=
find
(G.VV==x(
i
));
la1=G.Adj{a};
la0=[la0 la1];
end
end
|
运行结果:
path:
x =
abhgfcide
d= 37