您的位置:首页 > 其它

最小生成树——Prim算法

2016-12-14 10:02 225 查看
查看原文:http://www.wyblog.cn/2016/12/14/%e6%9c%80%e5%b0%8f%e7%94%9f%e6%88%90%e6%a0%91-prim%e7%ae%97%e6%b3%95/
一个无向图的最小生成树就是由该图的那些连接G的所有顶点的边构成的树,且其所有边权值之和最低。从定义可见,它是包含了图的所有顶点的最小树。
求解最小生成树经典算法即为Prim算法。
Prim算法是使得这个树一步步生长而成,因为最小生成树必定包含了所有顶点,所以可以任意选择起点。在生成树过程中的每一步,都要把一个节点当做根并往上加边。
Prim算法跟Dijkstra算法神似,它也是每一步中都去扫描还被标记为未知的那些顶点,然后找出dist最低的边,它一定是属于最小生成树的边,所以把这个顶点再标记为已知,再去更新跟这个顶点相临近的那些顶点的dist值。
在了解Prim算法之前,可以看看Dijkstra算法:
http://www.wyblog.cn/2016/12/09/%E5%B8%A6%E6%9D%83%E8%B7%AF%E5%BE%84%E6%9C%80%E7%9F%AD-dijkstra%E7%AE%97%E6%B3%95/
Prim算法的程序总体上跟Dijkstra算法是一样的,有少许区别。

最小生成树一般是对应的无向图,所以在构造图的时候,会把每条边认为是双向的,所以每条边要在图的邻接链表里添加两次。见代码里的CreateDAG函数。

顶点结构体里的dist所存的内容不同,在Dijkstra算法里,存的是最小路径之和,而在Prim算法里,存的是最小的权值。所以,在更新dist的代码差生了变化,变为了 $latex d_{\omega} = min ( d_{\omega},c_{\omega,v}) $。

打印最小生成树时,直接遍历所有顶点,并打印出对应的path值就可以了。

代码如下:

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>

using namespace std;

#define MAX_VERTEX_NUM 100
#define Vertextype int
#define Infinity 0xffff
#define NotAVertex -1

typedef struct EdgeNode
{
int adjVertex;
int weight; //邻接权重
EdgeNode *nextEdgeNode;
}EdgeNode;

typedef struct VerNode
{
int Known;
Vertextype data;
int dist;
int path;
EdgeNode *firstedge;
}VerNode;

typedef struct Graph
{
VerNode verNode[MAX_VERTEX_NUM];
int vertex_num,edge_num;
}Graph;

void CreateDAG(Graph &G,int n,int e)
{
int i,j,w,k;
G.vertex_num=n;
G.edge_num=e;
for(i=1;i<=n;i++)
{
cin>>G.verNode[i].data;
G.verNode[i].Known=0;
G.verNode[i].dist=Infinity;
G.verNode[i].path=-1;
G.verNode[i].firstedge=NULL;
}
for(k=1;k<=e;k++)  //无向图,所以邻接表要生成两份
{
EdgeNode *p;
cin>>i>>j>>w;

p=new EdgeNode;
p->adjVertex=j;
p->weight=w;
p->nextEdgeNode=G.verNode[i].firstedge;
G.verNode[i].firstedge=p;

p=new EdgeNode;
p->adjVertex=i;
p->weight=w;
p->nextEdgeNode=G.verNode[j].firstedge;
G.verNode[j].firstedge=p;
}
}

void Prim(Graph &G,int n,int start) //求最小生成树Prim算法
{
VerNode V;
EdgeNode *w;
int i,j,temp_num,temp_dist;
G.verNode[start].dist=0;
G.verNode[start].path=0;
for(i=1;i<=n;++i) //n个点待查找
{
temp_dist=Infinity;
temp_num=0;
for(j=1;j<=n;++j) //先寻找未知的最短顶点
{
if(G.verNode[j].Known==0 && G.verNode[j].dist<temp_dist)
{
temp_dist=G.verNode[j].dist;
temp_num=j;
}
}
G.verNode[temp_num].Known=1;
w=G.verNode[temp_num].firstedge;
while(w)
{
if(G.verNode[w->adjVertex].Known==0)
{
G.verNode[w->adjVertex].dist = min(G.verNode[w->adjVertex].dist,w->weight);
if(G.verNode[w->adjVertex].dist == w->weight) //说明权值被更新,那么就要更新路径标记
G.verNode[w->adjVertex].path=G.verNode[temp_num].data;
}
w=w->nextEdgeNode;
}
}
}

int PrintMST(Graph &G,int n) //打印最小生成树
{
for(int i=1;i<=n;i++)
printf("%d <=> %d\n",i,G.verNode[i].path);
}

int main()
{
Graph G;
CreateDAG(G,7,12); //给出图的顶点数及边数量
Prim(G,7,1); //给出顶点数以及起点编号
PrintMST(G,7); //给出顶点数,打印最小生成树
}

/********************

1 2 3 4 5 6 7
1 2 2
1 4 1
2 4 3
2 5 10
3 1 4
3 6 5
4 3 2
4 5 7
4 6 8
4 7 4
5 7 6
7 6 1

输出:
1 <=> 0
2 <=> 1
3 <=> 4
4 <=> 1
5 <=> 7
6 <=> 7
7 <=> 4
*********************/
查看原文:http://www.wyblog.cn/2016/12/14/%e6%9c%80%e5%b0%8f%e7%94%9f%e6%88%90%e6%a0%91-prim%e7%ae%97%e6%b3%95/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: