prim算法
prim算法其实和dijkstra算法在实现上基本上一样,也很容易理解。
prim算法的基本思想:
取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w。在添加的顶点 w 和已经在生成树上的顶点v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有 n个顶点为止。
在生成树的构造过程中,图中 n 个顶点分属两个集合:已落在生成树上的顶点集 U 和尚未落在生成树上的顶点集V-U ,则应在所有连通U中顶点和V-U中顶点的边中选取权值最小的边。
注意:prim算法是用于求无向图的最小生成树,但是偶不想画图,故依然拿了前面的有向图,但是大家可以将箭头去掉,全当无向图。
1 #include<iostream> 2 using namespace std; 3 4 const int MAXNUM=101; 5 6 //9*9 7 int MAP[MAXNUM][MAXNUM]={ 8 {INT_MAX, 2,INT_MAX,INT_MAX,INT_MAX, 9, 15,INT_MAX,INT_MAX}, 9 { 2,INT_MAX, 4,INT_MAX,INT_MAX,INT_MAX, 6,INT_MAX,INT_MAX}, 10 {INT_MAX, 4,INT_MAX, 2,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 15}, 11 {INT_MAX,INT_MAX, 2,INT_MAX, 1,INT_MAX,INT_MAX,INT_MAX, 1}, 12 {INT_MAX,INT_MAX,INT_MAX, 1,INT_MAX, 6,INT_MAX, 3,INT_MAX}, 13 { 9,INT_MAX,INT_MAX,INT_MAX, 6,INT_MAX,INT_MAX, 11,INT_MAX}, 14 { 15, 6,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 15, 2}, 15 {INT_MAX,INT_MAX,INT_MAX,INT_MAX, 3, 11, 15,INT_MAX, 4}, 16 {INT_MAX,INT_MAX, 15, 1,INT_MAX,INT_MAX, 2, 4,INT_MAX} 17 }; 18 19 int adjvex[MAXNUM]; //adjvex[i]=j表示j到i的路径 20 int lowcost[MAXNUM]; //lowcost[i]表示U集中顶点的顶点到V-U集中的i顶点的最短路径(一条边) 21 22 void prim(int s,int n) 23 { 24 int i; 25 bool mark[MAXNUM]={false}; //mark[i]==true表示i顶点属于U集 26 for(i=0;i<n;++i) 27 { 28 adjvex[i]=s; //表示s到i的路径 29 lowcost[i]=MAP[s][i]; 30 } 31 mark[s]=true; //源点s属于U集 32 for(i=2;i<n;++i) //循环n-2次就可以完成 33 { 34 int index=s,lowest=INT_MAX; 35 int j; 36 //在所有U集到V-U集的边中找出最小的一条 37 //mark[0~n]==false的边中找出 38 for(j=0;j<n;++j) 39 { 40 if(mark[j]==false&&lowcost[j]<lowest) 41 { 42 lowest=lowcost[j]; 43 index=j; 44 } 45 } 46 mark[index]=true; //加入index顶点到U集 47 //用新加入U集的顶点来更新lowcost的值 48 //既更新U集到V-U集边的值(mark[j]==false) 49 for(j=0;j<n;++j) 50 if(mark[j]==false&&MAP[index][j]<lowcost[j]) 51 { 52 lowcost[j]=MAP[index][j]; 53 adjvex[j]=index; //记录到j的路径中,j的前驱结点是index 54 } 55 } 56 } 57 58 59 void reverse(int a[],int n) 60 { 61 for(int i=0,j=n-1;i<j;++i,--j) 62 swap(a[i],a[j]); 63 } 64 65 int getpath(int a[],int i,int j) 66 { 67 int count=1; 68 a[0]=j; 69 while(i!=j) 70 j=a[count++]=adjvex[j]; 71 reverse(a,count); 72 return count; 73 } 74 75 int main() 76 { 77 int a[MAXNUM]; 78 prim(0,9); 79 int i; 80 for(i=1;i<9;++i) 81 { 82 cout<<"a到"<<char(i+'a')<<"的路径为:"; 83 int length=getpath(a,0,i); 84 for(int j=0;j<length;++j) 85 cout<<char(a[j]+'a')<<' '; 86 cout<<endl; 87 } 88 int s=0; 89 for(i=1;i<9;++i) 90 s+=lowcost[i]; 91 cout<<"最小生成树的权值和为:"; 92 cout<<s<<endl; 93 return 0; 94 }
prim算法和dijkstra算法的实现简直就是一模一样,除了数组有些不同,也很明显他们的效率很大程度取决于查找最小值的效率。
下为用队列实现的prim算法,和dijkstra的也一样,哈哈
1 void prim(int s,int n) 2 { 3 int q[MAXNUM],qf=0,ql=0; //建立一个队列用来存储未加入U集的顶点的下标 4 int i; 5 for(i=0;i<n;++i) 6 { 7 lowcost[i]=MAP[s][i]; 8 adjvex[i]=s; 9 if(i!=s) //将V-U集中的顶点的下标加入到队列q中 10 q[ql++]=i; 11 } 12 while(qf<ql-1) //循环n-2次就ok 13 { 14 int index_min=qf; 15 int j; 16 //q[qf~ql-1]指向的都是V-U集的顶点,找出lowcost最小 17 for(j=qf+1;j<ql;++j) 18 if(lowcost[q[j]]<lowcost[q[index_min]]) 19 index_min=j; 20 swap(q[index_min],q[qf]); //始终保持q[qf]指向U到V-U中最小的边 21 for(j=qf+1;j<ql;++j) //用新加入U集的顶点来更新U到V-U的边 22 if(MAP[q[qf]][q[j]]<lowcost[q[j]]) 23 { 24 lowcost[q[j]]=MAP[q[qf]][q[j]]; 25 adjvex[q[j]]=q[qf]; 26 } 27 ++qf; //注意 28 } 29 }