1.解析
Prim算法和Dijkstra算法非常类似,他们的伪码几乎相近,只是他们优先队列所排序的键值不同而已。Prim算法的键值为节点与集合S中顶点间的最轻边的权重,而在Dijkstra算法中,键值为由起始点到某节点的完整路径长度。
在后面的博客中会说明最小生成树MST与最短路径的区别。
2.代码实例
- #include<iostream>
- #include<malloc.h>
- #include<queue>
- #include <algorithm>
- #include<stdlib.h>
- #include<functional>
- using namespace std;
- #define maxNum 100 //定义邻接举证的最大定点数
- #define maxWeight 1000000 //边权最大值
- int cost[maxNum];//用户存储节点与最小生成树MST之间的权重,初始状态cost[i]=maxWeight
- int prev_Elem[maxNum];//该数组用户存储结点的前序,比如prev[v]=u,则表示u是v的前序
- int set[maxNum];//集合S,一开始为空,初始化的时候确定一个顶点,后续顶点都是通过prim算法找到的。set[i]=1表示顶点i属于集合s
- //顶点信息
- typedef struct
- {
- int id;//顶点编号
- int cost;//用于保存该顶点到MST的最小权值
- }node;
- //图的邻接矩阵表示结构
- typedef struct
- {
- //char v[maxNum];//图的顶点信息
- node v[maxNum];
- int e[maxNum][maxNum];//图的顶点信息
- int vNum;//顶点个数
- int eNum;//边的个数
- }graph;
- //函数声明
- void createGraph(graph *g);//创建图g
- void Prim(graph *g) ;//核心算法,是BFS的改版,只是将普通队列改成了优先队列。
- int cmp(node a,node b);//定义优先队列以升序还是降序排列
- int cmp(node a,node b)
- {
- return a.cost<b.cost;//升序
- }
- //prim算法
- void Prim(graph *g)
- {
- node Q[maxNum]; //定义结构体数组
- int front;//队列头
- int rear;//队列尾
- int count;//队列计数
- front=rear=count=0;//表示队列为空
- int k,i,j;
- //初始化cost的值
- for(i=1;i<=g->vNum;i++)
- {
- g->v[i].cost=maxWeight; //cost为最大值
- g->v[i].id=i;
- prev_Elem[i]=-1;
- set[i]=0;
- }
- g->v[1].cost=0;//1作为MST第一个顶点,初始化其cost为0
- //初始化优先队列
- for(i=1;i<=g->vNum;i++)
- {
- Q[++rear]=g->v[i];
- count++;//顶点进入队列Q
- }
- while(count>0)//队列不空 ,一直循环,也可是front<rear,也表示队列不空,count=rear-front
- {
- sort(Q+front+1,Q+rear+1,cmp);//对队列Q进行排序,按cost的升序排列。
- //以下两行是队列的出队操作
- node n1=Q[++front]; //取出当前非s集合中离s距离最近的点
- count--;//出队列操作
- k=n1.id;//
- cout<<k<<endl;
- set[k]=1;//将上述从队列中取出的顶点加入到集合s中去
- for(j=1;j<=g->vNum;j++)
- {
- if(g->e[k][j]!=maxWeight&&set[j]==0)//i->j之间有边,并且顶点j不属于集合s的时候
- {
- //如果顶点j到集合s的距离权值大于集合中一点k到j的距离,那么更新(update)j点距离权值
- //并令该j的前序为k
- if((g->v[j].cost>g->e[k][j]))
- //if((g->v[j].cost>g->e[k][j]))
- {
- g->v[j].cost=g->e[k][j];
- prev_Elem[j]=k;
- }
- }
- }
- //更新队列
- for(i=1;i<=g->vNum;i++)
- {
- Q[i]=g->v[i];
- }
- cout<<"更新cost后各顶点的cost值"<<endl;
- for(i=1;i<=g->vNum;i++)
- cout<<g->v[i].cost<<" ";
- cout<<endl;
- }
- }
- void createGraph(graph *g)//创建图g
- {
- cout<<"正在创建无向图..."<<endl;
- cout<<"请输入顶点个数vNum:";
- cin>>g->vNum;
- int i,j;
- //构造邻接矩阵,顶点到自身的距离是无穷大的。
- cout<<"输入邻接矩阵权值:"<<endl;
- for(i=1;i<=g->vNum;i++)
- for(j=1;j<=g->vNum;j++)
- {
- cin>>g->e[i][j];
- if(g->e[i][j]==0)
- g->e[i][j]=maxWeight;
- }
- }
- int main()
- {
- int i;
- graph *g;
- g=(graph*)malloc(sizeof(graph));
- createGraph(g);
- Prim(g);
- //for(int k=1; k<=g->vNum; k++)
- //{
- // cout<<g->v[k].cost<<" ";
- //}
- /*cout<<endl;*/
- cout<<"输出MST的各条边"<<endl;
- for(i=1;i<=g->vNum;i++)
- {
- //cout<<prev_Elem[i]<<" ";
- if(prev_Elem[i]!=-1)
- cout<<"存在边:"<<prev_Elem[i]<<"->"<<i<<",权值为:"<<g->e[prev_Elem[i]][i]<<endl;
- }
- //cout<<endl;
- system("pause");
- return 0;
- }
- /*
- 正在创建无向图...
- 请输入顶点个数vNum:6
- 输入邻接矩阵权值:
- 0 5 6 4 0 0
- 5 0 1 2 0 0
- 6 1 0 2 5 3
- 4 2 2 0 0 4
- 0 0 5 0 0 4
- 0 0 3 4 4 0
- 输出MST的各条边
- 存在边:3->2,权值为:1
- 存在边:4->3,权值为:2
- 存在边:1->4,权值为:4
- 存在边:6->5,权值为:4
- 存在边:3->6,权值为:3
- 请按任意键继续. . .
- */
3.总结kruskal算法和prim算法
简而言之,kruskal是找边的算法,prim算法是找点的算法。
3.1 kruskal算法基本思想:
kruskal算法每次选择n-1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。注意到所选取的边若产生环路则不可能形成一棵生成树。
kruskal算法分e 步,其中e 是网络中边的数目。按耗费(边的权值)递增的顺序来考虑这e 条边,每次考虑一条边。当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。
3.2 Prim算法的基本思想是:
1) 在图G=(V, E) (V表示顶点 ,E表示边)中,从集合V中任取一个顶点(例如取顶点v0)放入集合 U中,这时 U={v0},集合T(E)为空。
2) 从v0出发寻找与U中顶点相邻(另一顶点在V中)权值最小的边的另一顶点v1,并使v1加入U。即U={v0,v1 },同时将该边加入集合T(E)中。
3) 重复(2),直到U = V为止。
这时T(E)中有n-1条边,T = (U, T(E))就是一棵最小生成树。
本文转自xwdreamer博客园博客,原文链接:http://www.cnblogs.com/xwdreamer/archive/2011/06/16/2296997.html,如需转载请自行联系原作者