最小生成树之Prim算法 优先队列版本

来源:互联网 发布:mysql中full join报错 编辑:程序博客网 时间:2024/05/29 18:28

http://www.cppblog.com/tanky-woo/archive/2013/02/18/126940.html

这个博客对prim讲解的很好


如果用传统的做法每次都要将集合内的元素遍历一遍,再与集合外的元素判断是否相连,再找出最小权值,然后把带有最小权值的那个终点记录下来,插入到集合内。

下次循环再从集合的第一个元素开始遍历,这样会浪费很多时间,于是乎,我想能不能把之前已经遍历过的顶点而且不是最小权的那些顶点存入列表中,下次再遍历的时候可以接着用,只需要把新的顶点插入进去就行了,那么怎么输出最小的权呢,大家都知道有个优先队列,就是把队列中的权值按由小到大排列,每次只需要把top()的权值pop掉就行了。



说了这么多,我们举个栗子(图就用上面那个博客的prim图)

假如从顶点1开始,与1相连的有1-2  1-3  1-4   找到1-3的权值为1 最小,于是把3这个终点的所有的相连的边插入到队列中,即3-5 3-6 3-2 3-4 因为1这个顶点已经被标记过所以,

3-1就不插入了,所以现在的队列中有 1-2 1-4 3-2 3-4 3-5 3-6 再找到top的最小的权值为3-6这个边的权4,所以把6记录下来,把3-6这个边pop掉,再重复之前的找和6相连的边。。。。。


我在写代码的时候遇到好几个问题。


   比如我在把3插入队列之后,怎么不用遍历找出和3相连的边,解决办法就是用代码中的link数组来保存与3对应的边的终点,

    那么和以3为起点的边有很多,那有几个呢,link[p][link[p][0]++] = q;这个就是解决办法每有重复的顶点插入进去,link[p][0]就会++,最后link[p][0]就是与p顶点相连的边数,

    link[3][1] = 2  (表示以3作为顶点的第一条边的终点为2 即 3-2这个边) 

   link[3][2] = 4  同理表示 3-4为以3作为顶点的第二条边


下面贴出我的代码

#include <iostream>#include <queue>#include <map>#include <cstring>using namespace std;#define maxint 0x3f3f3f3f#define maxnum 1051int link[maxnum][maxnum];int c[maxnum][maxnum];int sum,n;//sum为最小权之和,n为顶点个数struct node{int s;//起点int e;//终点int w;//权};bool operator < (const node &a,const node &b){return a.w > b.w;}void prim(int s){int i,j,k,m,t,u,total;int vis[maxnum];memset(vis,0,sizeof(vis));priority_queue <node> qq;struct node nn;total  = 1;vis[s] = 1;sum = 0;while(total < n){for(i=1;i<link[s][0];i++){if(!vis[link[s][i]]){nn.s = s;nn.e = link[s][i];nn.w = c[s][nn.e];qq.push(nn);}}while(!qq.empty() && vis[qq.top().e])//遇到顶点和集合外的顶点没有相连的qq.pop();//刚巧这个点作为终点是最短的,因为这个顶点没背标记过,所以会错误的计入在内nn = qq.top();s = nn.e;sum += nn.w;//cout<<nn.s<<" "<<nn.e<<" "<<nn.w<<endl;vis[s] = 1;//标记为集合内的元素qq.pop();total++;}}int main(){int i,j,k;int line,len;int t,s,d,p,q;cin>>n>>line;for(i=1;i<=n;i++){link[i][0] = 1;}for(i=1;i<=line;i++){cin>>p>>q>>len;c[p][q] = c[q][p] = len;link[p][link[p][0]++] = q;link[q][link[q][0]++] = p;}cin>>s;//输入起始点prim(s);cout<<sum<<endl;return 0;}

如有错误 欢迎指正

0 0
原创粉丝点击