什么是最小生成树?
生成树的代价: 设G=(V,E)是一个无向连通网,生成树上各边的权值之和叫做该生成树的代价。
最小生成树: 图G的所有生成树中,代价最小的生成树,叫做最小生成树。
构造最小生成树的算法
1.Prim算法(加点法)
2.Kruskal算法(加边法)
1.Prim算法
基本思想:
1、创建一个顶点集合U,和一个边集合TE。
2、若从u开始,则将u放入U中,V总顶点集合则少一个u,
3、在u∈U,v∈V-U的两个顶点集合里找出代价最小的边(u,v),加入边集TE,同时把v加入U中。重复操作。
数据结构设计
设数组adjvex[n]表示候选最短边的领接点
设数组lowcast[n]表示权值
其值如下,含义是候选最短边(i,j)的权值为w,其中i∈V-U , j∈U
adjvex[i] = j
lowcost[i] = w
伪代码
1.初始化两个辅助数组lowcost和adjvex。
2.输出顶点u,将定点u加入集合U中。
3.重复执行以下操作n-1次:
3.1在lowcost中选取最短的边(lowcost[k]),获取对应的顶点序号k。
3.2输出顶点k和对应的权值。
3.3将顶点k加入集合U中;
3.4调整数组的lowcost和adjvex
上代码
void Prim(Graph G){
for(int i=0;i<G.vertexNum;i++){
lowcost[i]=G.arc[0][i];adjvex[i]=0; //初始化辅助数组,从第一个节点(下标为0的点)开始
}
lowcost[0]=0; //第一个顶点放入集合U中
for(int i=1;i<G.vertexNum;i++){ //循环n-1次
k=MinEdge(lowcost,G.vertextNum); //找出权值最小的边,返回边(0,k)的终点k
cout<<k<<adjvex[k]<<lowcost[k]; //输出k,k的最短边邻接点,最短边的权值
lowcost[k]=0; //k放入集合U
for(int j=1;j<G.vertexNum;j++){ //修改lowcost和adjvex的数据
if(G.arc[k][j]<lowcost[j]){ //遍历所有点与k邻接的点所成的边,若arc[k][j]比之前的lowcost更小,必须更新数据。
lowcost[j]=G.arc[k][j];//最小边的权值修改
adjvex[j]=k; //最短边的邻接点也许修改,j的修改成k。(原来是初始点0,需更新成k。)
}
}
}
}
//下面是求出最小边的邻接点终点k的函数
int MinEdge(int *lowcost, int num) {
int min = 100000; //写一个较大的数
int index = -1; //初始化下标
for(int i = 0; i < num; i++) { //n次循环找出最小
if(lowcost[i] < min && lowcost[i] != 0) {
min = lowcost[i]; //最小权值找到
index = i; //最小边的邻接终点,循环结束后返回出去
}
}
return index;
}
2.Kruskal算法
基本思想:
1.将图中所有的边按权值大小排序。
2.从最小的边开始选,所选边与已选边处于不同连通分量则可,处于同一个连通分量就舍弃该边,继续选后面的,以免构成回路。
3.重复下去,若最后连通分量为1,则构造完成最小生成树
伪代码
1.初始化:U=V,TE={ };
2.循环直到连通分量为1
2.1在E中寻找最短边(u,v);
2.2若两边位于不同连通分量,则
2.2.1将边(u,v)并入TE;并且将两个连通分量合为一个;
2.3 标记(u,v),防止之后再被选取;
Kruskal算法实现中的三个关键问题
1.图的存储结构是边集数组
2.如何判断两个边在不同的连通分量上?
用到并查集,定义parent[i]数组,数组的值表示双亲节点(初始值为-1);
当一条边的两个顶点的根节点不同时,这两个点属于不同的连通分量
(利用parent数组查找树的根节点,当一个节点n的parent为-1时,根节点就是n)
3.连通分量的合并?
一个分量的根节点使v1,另一个使v2,使parent[v2]=v1;实现连通.