《数据结构》第8章 图 P222
例8.8 利用狄克斯特拉算法求最小生成树
首先说几个概念:
1、在无向图G中,若从订单v i 到顶点v j 有路径,则称v i 和v j 是连通的。
2、一个连通图的生成树是一个极小连通子图,它含有图中全部顶点,但只有构成一颗树的(n-1)条边。图的所有生成树中具有边上的权值之和最小的树称为图的最小生成树。
3、在一个无权的图中,若从一顶点到另一顶点存在着一条路径,称该路径上所有经过的边的数目为该路径长度,它等于该路径上的顶点数减1。
把路径长度最短的那条路径叫做最短路径。
而狄克斯特拉算法就是求在一个图中,从指定顶点到其他顶点的最短路径。
书中源代码:
#include <stdio.h>
#define
MaxSize 100
#define
INF 32767
//
INF表示∞
#define
MAXV 100
//
最大顶点个数
typedef
int
InfoType;
typedef
struct
{
int
no;
//
顶点编号
InfoType info;
//
顶点其他信息
} VertexType;
//
顶点类型
typedef
struct
//
图的定义
{
int
edges[MAXV][MAXV];
//
邻接矩阵
int
n,e;
//
顶点数,弧数
VertexType vexs[MAXV];
//
存放顶点信息
} MGraph;
//
图的邻接矩阵类型
void
Ppath(
int
path[],
int
i,
int
v)
//
前向递归查找路径上的顶点
{
int
k;
k=path[i];
if
(k==v)
return
;
//
找到了起点则返回
Ppath(path,k,v);
//
找顶点k的前一个顶点
printf(
"
%d,
"
,k);
//
输出顶点k
}
void
Dispath(
int
dist[],
int
path[],
int
s[],
int
n,
int
v)
{
int
i;
for
(i=
0
;i<n;i++)
if
(s[i]==
1
)
{
printf(
"
从%d到%d的最短路径长度为:%d\t路径为:
"
,v,i,dist[i]);
printf(
"
%d,
"
,v);
//
输出路径上的起点
Ppath(path,i,v);
//
输出路径上的中间点
printf(
"
%d\n
"
,i);
//
输出路径上的终点
}
else
printf(
"
从%d到%d不存在路径\n
"
,v,i);
}
void
Dijkstra(MGraph g,
int
v) //狄克斯特拉算法
{
int
dist[MAXV],path[MAXV];
int
s[MAXV];
int
mindis,i,j,u;
for
(i=
0
;i<g.n;i++)
{
dist[i]=g.edges[v][i];
//
距离初始化
s[i]=
0
;
//
s[]置空
if
(g.edges[v][i]<INF)
//
路径初始化
path[i]=v;
else
path[i]=-
1
;
}
s[v]=
1
;path[v]=
0
;
//
源点编号v放入s中
for
(i=
0
;i<g.n;i++)
//
循环直到所有顶点的最短路径都求出
{
mindis=INF;
//
mindis置最小长度初值
for
(j=
0
;j<g.n;j++)
//
选取不在s中且具有最小距离的顶点u
if
(s[j]==
0
&& dist[j]<mindis)
{
u=j;
mindis=dist[j];
}
s[u]=
1
;
//
顶点u加入s中
for
(j=
0
;j<g.n;j++)
//
修改不在s中的顶点的距离
if
(s[j]==
0
)
if
(g.edges[u][j]<INF && dist[u]+g.edges[u][j]<dist[j])
{
dist[j]=dist[u]+g.edges[u][j];
path[j]=u;
}
}
Dispath(dist,path,s,g.n,v);
//
输出最短路径
}
void
main()
{
int
i,j;
MGraph g;
g.n=
7
;g.e=
12
;
int
a[
7
][MAXV]={
{
0
,
4
,
6
,
6
,INF,INF,INF},
{INF,
0
,
1
,INF,
7
,INF,INF},
{INF,INF,
0
,INF,
6
,
4
,INF},
{INF,INF,
2
,
0
,INF,
5
,INF},
{INF,INF,INF,INF,
0
,INF,
6
},
{INF,INF,INF,INF,
1
,
0
,
8
},
{INF,INF,INF,INF,INF,INF,
0
}};
for
(i=
0
;i<g.n;i++)
//
建立图9.16所示的图的邻接矩阵
for
(j=
0
;j<g.n;j++)
g.edges[i][j]=a[i][j];
printf(
"
最短路径
:\n
"
);
Dijkstra(g,
0
);
printf(
"
\n
"
);
}
书上说:
解 利用狄克斯特拉算法可以求出从指定顶点(源点)到其他顶点的最短路径,而最小生成树是以源点为根,其路径权值之和最小的生成树,因此,由源点到所有其他顶点的最短路径上的不重复出
现的边构成了最小的生成树的所有边。
具体代码:
#include <stdio.h>
#define
MaxSize 100
#define
INF 32767
//
INF表示∞
#define
MAXV 100
//
最大顶点个数
typedef
int
InfoType;
typedef
struct
{
int
no;
//
顶点编号
InfoType info;
//
顶点其他信息
} VertexType;
//
顶点类型
typedef
struct
//
图的定义
{
int
edges[MAXV][MAXV];
//
邻接矩阵
int
n,e;
//
顶点数,弧数
VertexType vexs[MAXV];
//
存放顶点信息
} MGraph;
//
图的邻接矩阵类型
void
DisMinTree(
int
path[],
int
s[],
int
n,
int
v)
//
由path求最小的生成树
{
int
i,pre,j,k;
int
edges[MAXV][
2
],edgenum=
0
;
//
edges数组用于存放最小生成树的所有边,
//
edges[i][0]存放第i条边的起点,edges[i][1]存放第i条边的终点
printf(
"
最小生成树的所有边:\n\t
"
);
for
(i=
0
;i<n;i++)
if
(s[i]==
1
&& i!=v)
{
j=i;
pre=path[i];
do
//
搜索最短路径生成最小生成树的所有边
{
if
(edgenum==
0
)
//
将(pre,j)边加入到edges中
{ edges[edgenum][
0
]=pre;
edges[edgenum][
1
]=j;
edgenum++;
}
else
{
k=
0
;
while
(k<edgenum&&(edges[k][
0
]!=pre||edges[k][
1
]!=j))
k++;
if
(k>=edgenum)
//
(pre,j)边未在edges中时加入
{
edges[edgenum][
0
]=pre;
edges[edgenum][
1
]=j;
edgenum++;
}
}
j=pre;
pre=path[pre];
}
while
(pre!=v);
}
for
(k=
0
;k<edgenum;k++)
printf(
"
(%d,%d)
"
,edges[k][
0
],edges[k][
1
]);
printf(
"
\n
"
);
}
void
Dijkstra(MGraph g,
int
v)
{
int
dist[MAXV],path[MAXV];
int
s[MAXV];
int
mindis,i,j,u;
for
(i=
0
;i<g.n;i++)
{
dist[i]=g.edges[v][i];
//
距离初始化
s[i]=
0
;
//
s[]置空
if
(g.edges[v][i]<INF)
//
路径初始化
path[i]=v;
else
path[i]=-
1
;
}
s[v]=
1
;path[v]=
0
;
//
源点编号v放入s中
for
(i=
0
;i<g.n;i++)
//
循环直到所有顶点的最短路径都求出
{
mindis=INF;
//
mindis置最小长度初值
for
(j=
0
;j<g.n;j++)
//
选取不在s中且具有最小距离的顶点u
if
(s[j]==
0
&& dist[j]<mindis)
{
u=j;
mindis=dist[j];
}
s[u]=
1
;
//
顶点u加入s中
for
(j=
0
;j<g.n;j++)
//
修改不在s中的顶点的距离
if
(s[j]==
0
)
if
(g.edges[u][j]<INF && dist[u]+g.edges[u][j]<dist[j])
{
dist[j]=dist[u]+g.edges[u][j];
path[j]=u;
}
}
DisMinTree(path,s,g.n,v);
//
输出最小生成树
}
void
main()
{
int
i,j;
MGraph g;
g.n=
7
;g.e=
12
;
int
a[
7
][MAXV]={
{
0
,
4
,
6
,
6
,INF,INF,INF},
{INF,
0
,
1
,INF,
7
,INF,INF},
{INF,INF,
0
,INF,
6
,
4
,INF},
{INF,INF,
2
,
0
,INF,
5
,INF},
{INF,INF,INF,INF,
0
,INF,
6
},
{INF,INF,INF,INF,
1
,
0
,
8
},
{INF,INF,INF,INF,INF,INF,
0
}};
for
(i=
0
;i<g.n;i++)
//
建立图9.16所示的图的邻接矩阵
for
(j=
0
;j<g.n;j++)
g.edges[i][j]=a[i][j];
Dijkstra(g,
0
);
printf(
"
\n
"
);
}
个人感觉不太理解,然后想了想,觉得书上这样解释有点笼统,不易于理解,于是,自己把自己的理解写下来。
假设有图G,其中有顶点A、B、C、D...,开始利用狄克斯特拉算法从顶点A开始查找最短路径,首先找到B,如下图1:
图1
然后,从【A-C:2】 【B-C:(无穷)】【A-D:1】【B-D:3】中查找权值最小的也就是A-D,这时我们可以把A、B(左圆)看成一个系统AB,然后AB到C的距离是2,到D的距离是1,
所以得到图2:
图2
然后把D添加到左圆(已找到最短路径),如图3:
图3
然后得到系统ABD到C的距离是2,然后依次添加顶点,直到右圆为空,即所有顶点均已找到最短路径。
以上就是狄克斯特拉算法求的从指定顶点(A)到其他顶点最短路径的算法,其实里面已经包含了求最小生成树的算法,为什么这样说呢,请看下面解释。
图2中左圆也就是系统AB可以看成一个只有A和B两个顶点的图,那么AB也就是这个图的最小生成树了,然后现在又添加了顶点D(因为A-D、B-D和A-C中最短是A-D也就是顶点D),这时有两种选择
A-D(1)和B-D(3),所以按照最小生成树法则,选择A-D(1)作为代表,代表系统(AB)和D的桥梁。这时形成图3,而系统AB也变成了系统ABD,而系统ABD同样还是一个只有三个顶点A、B和D的图的最小生成树,所以按照狄克斯特拉算法得到的就是指定图的最小生成树!
是个人理解,可能有些愚钝,肯定有更好地理解方法,望大家指教!
2012.02.21

