算法的阶梯

最小生成树Prim+heap(主)

2019-04-27  本文已影响0人  董玉恒_算法训练营

                                           最小生成树

    对于一个连通网(连通带权图,假定每条 边上的权均为大于零的实数)来说,每棵 树的权(即树中所有边的权值总和)也可能不同 具有权最小的生成树称为最小生成树。

       最小生成树问题主要有三个算法;分别是:prim算法,Kruskal算法以及将prim与堆结合的prim+heap算法。

  参考资料 :    三种算法的效率比较,以及各个情况下的选择

   本文是三种算法中最复杂的prim+heap算法,本文尝试对每句代码进行注解,将我的理解尽可能详细地表达出来。

#include<iostream>

#include<cstdio>//本头文件在程序中可以不加,本次加上是由于个人习惯

                            //(也就是说我还处于半c++半c状态)

#include<queue>

#include<vector>

#include<algorithm>

using namespace std;

const int INF=1<<30;//定义一个“无穷大的”变量,相当于对1进行30次左移运算 ,2^30.

 //相关资料:位运算

struct Edge{//定义结构体,对一个节点的临接点及路径权值进行记录

int v;

int w;

Edge(int v_,int w_):v(v_),w(w_){}//相当于定义一个函数(或说是一种方法)对v,w赋初值

bool operator<(const Edge&a)const{//这里有一些试错

return w>a.w;//在后面的队列里,边权值越小越优先 

                    //(至于为什么要用,读到后面就可以明白了)

}

};//这一块有问题不是很清楚,打算以后再填这个坑

vector<vector<Edge> >G(100);//利用vector建立二维数组 ,此处有一个注意点就是:

                                          // “vector<vector<Edge>(此处必须有空格)>G(100)”,

                                          //因为"> >"和输入要用的">>"过于相似

int HeapPrim(const vector<vector<Edge> >G,int n){//G为图,n为途中所有结点的数量

int Toln=0;//定义最小生成树中的结点值 

                //与n不同的是“Toln”标识的是当前“最小生成树”中的节点数量

int Tolw=0;//定义最小生成树的(当前)最小权值

vector<int>vUsed(n);//定义数组,标识该点是否在最小生成树中

vector<int>vDist(n);//定义数组,标识每个最小生成树外节点与最小生成树的距离

Edge xDist(0,0);//定义变量;

priority_queue<Edge>pq;//定义优先队列

pq.push(Edge(0,0));//将一个节点压入优先队列 

for(int i=0;i<n;i++){

vUsed[i]=0;//所有结点没有被访问

vDist[i]=INF;//赋值前所有结点间的距离都为无穷大

}

while(Toln<n&&!pq.empty()){

do{//"do{}while;"语句,如果节点被访问,且队列非空的情况下

xDist=pq.top();//将队列中的第一个赋值给xDist变量,这也就是为什么在上面定义结构体时 

                        //要重载“bool operator<”,在本优先队列中,边权值小的永远在前面

                        //这里xDist取第一个值,那就是取队列中边权值最小的

pq.pop();//删除第一个值;

}while(vUsed[xDist.v]==1&&!pq.empty());

if(vUsed[xDist.v]==0){//节点没有被访问

Toln++;//Toln加一

vUsed[xDist.v]=1;//标记已在最小生成树中

Tolw+=xDist.w;//最小生成树权值增加

for(int i=0;i<G[xDist.v].size();i++){//对xDist.v的邻接点的数据进行更新

int k=G[xDist.v][i].v;//k为与xDist.v相接的点 ,从第一个相接的点遍历至最后一个

if(vUsed[k]==0){//如果k没有被访问过,就进行操作

int w=G[xDist.v][i].w;//w为xDist.v 和节点k的距离

if(w<vDist[k]){//如果w小于节点k距离最小生成树的距离,就对vDist[k]进行更新

vDist[k]=w;

pq.push(Edge(k,w));//只有 w小于节点k距离最小生成树的距离的时候,再将k点压入队列

                    }

                }

            }

        }

}

if(Toln<n)

return -1;

else

return Tolw;

}

int main(){

int n;

while(cin>>n){

 for(int i=0;i<n;i++)

 G[i].clear();

//G.clear(); //此处使用有误,可能会由于前面的各种东西太多。

                //导致此步错误,如果使用的是此语句。

                //那么程序可以通过调试,但无法给出答案

for(int i=0;i<n;i++)

for(int j=0;j<n;j++){

int w;

cin>>w;

G[i].push_back(Edge(j,w));

}

cout<<HeapPrim(G,n)<<endl;

}

return 0;

}

上一篇下一篇

猜你喜欢

热点阅读