- 算法思想
设G=(V,E)是具有n个顶点的连通图,T=(U,TE)是G的最小生成树.其中V和U分别是图和树的定点集合,E和TE分别是图和树的边的集合.初始,T树为空,即U和TE都是空集.首先选择图中的任意顶点作为根,加入到集合U中,然后每加入一个顶点,都必须加入一条以树已有顶点和新加入顶点连成的边.这样才能既保证连通同时又不产生回路.这样的添加工作只要进行n-1次. 算法分析
使用贪心算法,每次向中添加边(u,v)时,都能够保证边的一个顶点u属于集合U,而另外一个顶点属于V-U集合.也就是说U是V的子集,当算法结束是U=V
该边是具有改性质的边中权值最小的边.设若|U|=k,|V|=n,则可能具有m=k*(n-k)条边,检索 m条边的时间是不可忽略的.因此我们使用了edges[N]去解决这个问题.它记录了如下信息:formvex,endvex,weight,即一条边上的两个顶点和权值.
使用edges[N]表示边集数组,下标为0的元素不使用,以0号顶点为树根.在图G6中,顶点编号为0~6,初始值(k=0),edges[1]到edges[6]分别存放着从0号顶点到1~6号顶点的各条边,若此边确实存在,则边上的权值为实际值,否则为INF.该数组保留着从树中到数外的(n-k)条当前权值最小的边.表格中第k(k>=1)行表示添加第k条边后边集数组edges各个分量的当前值edges[i],i是边集数组的下标.从k-1行选取实际权值最小的边加入到树中.采取将当前最短边edges[m]与边edges[k]交换的方式,这样就把数组划分成了两个部分,下标1~k表示已经加入树的边,从k+1到n表示未加入树的边.这样每次进行修改的时候仅仅是在k+1到n范围内进行.
分析k=1的情况:首先从k-1行中找到权值最小的边,一个顶点在树内,一个顶点在树外,该边为(0,3) 权值5 把它交换到下标为1的位置上.顶点3的加入,进行数组的更新:{(0,1) 8}由{(3,1) 3}代替;{(0,5) -}由{(3,5) 7}代替;((0,6) -)由{(3,6) 15代替}
……
以下不再赘述.- 代码实现
#include <stdio.h>
#include <stdlib.h>
#include <vector>
using namespace std;
const int MAX_E=1000;
typedef struct edges{
int fromvex;
int endvex;
int weight;
}Edges;
Edges edges[MAX_E];
vector<int> G[MAX_E];
void prim0(){
int i,min,m
//初始化1~n个元素
/*
给边集数组赋初值,因为取0号元素为树根,可以不包含0号元素.
*/
for(i=1;i<n;i++){
edges[i].fromvex = 0; //边的起始顶点为树种的0号元素
edges[i].endvex = i; //边的终止顶点为树外的1~n号顶点
edges[i].weight = G[0][i]; //每条边上的权值
}
for(i=1;i<n;i++){ //向树中添加边的循环
min=edges[i].weight; m=i;
for(j=i+1;j<n;j++){
if(adges[j].weight<min){
min=edges[j].weight; m=j;
}
} //找到边集数组中,下标为m的边权值最小
if(k!=m){
Edges temp = edges[m]; edges[m]=edges[k]; edges[k]=temp;
} /*交换m和k号元素*/
j = adges[k].endvex; //j为新加入树的第k条边的终端顶点
for(t=i+1;t<n;t++){//对尚未加入树的n-k条边进行调整
int s = edges[t].endvex; int e = G[j][s];
/*若树中加入顶点j前到树外顶点s的最短边没有j到s短,则用后者更新它*/
if(w<edges[t].weight){
edges[s].weight = e;
edges[s].fromvex = j;
}
}
}
}
/*
该算法的复杂度为O(n2);
*/
//集合的理解方式
int cost[MAX_E][MAX_E]; //cist[u][v]表示边e=(u,v)的权值(不存在的情况下为INF)
int mincost[MAX_E]; //从集合X出发的边到每一个顶点的最小值
bool used[MAX_E]; //顶点i是否已经包含在生成树X中
int V; //顶点的个数
int prim1(){
for(int i=0;i<V;i++){
mincost[i]=INF;
used[i]=false;
}
minconst[0]=0;
used[0]=true;
int ans=0; //最短路径长度
while(true){
int v=-1;
//从不属于集合X中的顶点中选出从X到其权值最小的顶点
for(int i=0;i<V;i++){
if(!used[i] &&(v==-1 || mincost[i]<mincost[v]) v=i;
}
if(v==-1){//所有顶点都已经访问完毕,或者是没有路径
break;
}
used[v]=true; //把顶点v加入到集合X中
ans += mincost[v]; //把长度加入到结果中
for(int i=0;i<V;i++){
mincost[i]=min(mincost[i],cost[v][i]);
}
}
return ans;
}
/*
该算法的复杂度仍旧为O(n2);
*/