prim算法其实和dijkstra算法在实现上基本上一样,也很容易理解。
prim算法的基本思想:
取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w。在添加的顶点 w 和已经在生成树上的顶点v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有 n个顶点为止。
在生成树的构造过程中,图中 n 个顶点分属两个集合:已落在生成树上的顶点集 U 和尚未落在生成树上的顶点集V-U ,则应在所有连通U中顶点和V-U中顶点的边中选取权值最小的边。
注意:prim算法是用于求无向图的最小生成树,但是偶不想画图,故依然拿了前面的有向图,但是大家可以将箭头去掉,全当无向图。
#include<iostream> using namespace std; const int MAXNUM=101; //9*9 int MAP[MAXNUM][MAXNUM]={ {INT_MAX, 2,INT_MAX,INT_MAX,INT_MAX, 9, 15,INT_MAX,INT_MAX}, { 2,INT_MAX, 4,INT_MAX,INT_MAX,INT_MAX, 6,INT_MAX,INT_MAX}, {INT_MAX, 4,INT_MAX, 2,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 15}, {INT_MAX,INT_MAX, 2,INT_MAX, 1,INT_MAX,INT_MAX,INT_MAX, 1}, {INT_MAX,INT_MAX,INT_MAX, 1,INT_MAX, 6,INT_MAX, 3,INT_MAX}, { 9,INT_MAX,INT_MAX,INT_MAX, 6,INT_MAX,INT_MAX, 11,INT_MAX}, { 15, 6,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 15, 2}, {INT_MAX,INT_MAX,INT_MAX,INT_MAX, 3, 11, 15,INT_MAX, 4}, {INT_MAX,INT_MAX, 15, 1,INT_MAX,INT_MAX, 2, 4,INT_MAX} }; int adjvex[MAXNUM]; //adjvex[i]=j表示j到i的路径 int lowcost[MAXNUM]; //lowcost[i]表示U集中顶点的顶点到V-U集中的i顶点的最短路径(一条边) void prim(int s,int n) { int i; bool mark[MAXNUM]={false}; //mark[i]==true表示i顶点属于U集 for(i=0;i<n;++i) { adjvex[i]=s; //表示s到i的路径 lowcost[i]=MAP[s][i]; } mark[s]=true; //源点s属于U集 for(i=2;i<n;++i) //循环n-2次就可以完成 { int index=s,lowest=INT_MAX; int j; //在所有U集到V-U集的边中找出最小的一条 //mark[0~n]==false的边中找出 for(j=0;j<n;++j) { if(mark[j]==false&&lowcost[j]<lowest) { lowest=lowcost[j]; index=j; } } mark[index]=true; //加入index顶点到U集 //用新加入U集的顶点来更新lowcost的值 //既更新U集到V-U集边的值(mark[j]==false) for(j=0;j<n;++j) if(mark[j]==false&&MAP[index][j]<lowcost[j]) { lowcost[j]=MAP[index][j]; adjvex[j]=index; //记录到j的路径中,j的前驱结点是index } } } void reverse(int a[],int n) { for(int i=0,j=n-1;i<j;++i,--j) swap(a[i],a[j]); } int getpath(int a[],int i,int j) { int count=1; a[0]=j; while(i!=j) j=a[count++]=adjvex[j]; reverse(a,count); return count; } int main() { int a[MAXNUM]; prim(0,9); int i; for(i=1;i<9;++i) { cout<<"a到"<<char(i+'a')<<"的路径为:"; int length=getpath(a,0,i); for(int j=0;j<length;++j) cout<<char(a[j]+'a')<<' '; cout<<endl; } int s=0; for(i=1;i<9;++i) s+=lowcost[i]; cout<<"最小生成树的权值和为:"; cout<<s<<endl; return 0; }
prim算法和dijkstra算法的实现简直就是一模一样,除了数组有些不同,也很明显他们的效率很大程度取决于查找最小值的效率。
下为用队列实现的prim算法,和dijkstra的也一样,哈哈
void prim(int s,int n) { int q[MAXNUM],qf=0,ql=0; //建立一个队列用来存储未加入U集的顶点的下标 int i; for(i=0;i<n;++i) { lowcost[i]=MAP[s][i]; adjvex[i]=s; if(i!=s) //将V-U集中的顶点的下标加入到队列q中 q[ql++]=i; } while(qf<ql-1) //循环n-2次就ok { int index_min=qf; int j; //q[qf~ql-1]指向的都是V-U集的顶点,找出lowcost最小 for(j=qf+1;j<ql;++j) if(lowcost[q[j]]<lowcost[q[index_min]]) index_min=j; swap(q[index_min],q[qf]); //始终保持q[qf]指向U到V-U中最小的边 for(j=qf+1;j<ql;++j) //用新加入U集的顶点来更新U到V-U的边 if(MAP[q[qf]][q[j]]<lowcost[q[j]]) { lowcost[q[j]]=MAP[q[qf]][q[j]]; adjvex[q[j]]=q[qf]; } ++qf; //注意 } }