您的位置:首页 > 其它

图论;最小生成树;普利姆算法;贪心策略;可用最小堆实现;

2010-04-03 22:48 525 查看
#include <iostream>
using namespace std;

#define MAX 1000 //用于表示顶点之间没有联系

//无向图的邻接矩阵定义
typedef struct
{
char *vertics;//存放图中结点
int **edge;//存放邻接矩阵
int num;//存放图中顶点数量
}Graph;

typedef struct
{
char vertics;//顶点信息
int weight;//权值
}MinSpanTree;//记录最小生成树的n个结点的信息

//这里用了简单的lowCost[]数组记录当前可选顶点到生成树上的最小距离,更好的办法是利用最小堆
//不断的取当前与生成树具有最小距离的顶点加入到生成树中,更新堆中的每个顶点对应的距离,从而免去了每次都用O(n)时间
//从lowcost[]中取最小距离顶点的时间和判断顶点是否已经加入生成树的if语句

class Prim
{
private:
Graph graph;//定义图
MinSpanTree *tree;//记录最小生成树的信息
public:
//构造函数
Prim(int num)
{
tree=new MinSpanTree[num+1];
graph.vertics=new char[num+1];
graph.edge=new int* [num+1];
for(int i=0;i<=num;i++)
{
graph.edge[i]=new int[num+1];
}
graph.num=num;
}
//普里姆算法
void prim()
{
int min;//用于记录最小lowCost
int minPoint;//用于记录最小lowCost的顶点编号
int *lowCost=new int[graph.num+1];//num个顶点到生成树上的最小花费,MAX表示与生成树无联系或者已经加入到生成树中
//初始化第一个顶点
lowCost[1]=-1;//花费设置为-1,表示已加入生成树
tree[1].vertics=graph.vertics[1];
tree[1].weight=0;
//初始化其他顶点到生成树的花费
for(int i=2;i<=graph.num;i++)
{
lowCost[i]=graph.edge[1][i];
}
//一共加入num-1个顶点(加上第一个顶点则为num个)构成最小生成树
int count=2;
while(count<=graph.num)
{
min=MAX;
//找出距离生成树距离最小的且没有加入生成树的点
for(int i=2;i<=graph.num;i++)
{
if(lowCost[i]!=-1&&lowCost[i]<min)
{
min=lowCost[i];
minPoint=i;
}
}
//如果不存在点与生成树有联系
if(min==MAX)
{
cout<<"无法生成最小生成树"<<endl;
return;
}
//记录第count个加入生成树的顶点的信息
tree[count].vertics=graph.vertics[minPoint];
tree[count].weight=lowCost[minPoint];
//设置该顶点的lowCost为MAX,表示已加入生成树
lowCost[minPoint]=-1;
//更新lowCost[]
for(int i=2;i<=graph.num;i++)
{
if(lowCost[i]!=-1&&graph.edge[minPoint][i]<lowCost[i])
{
lowCost[i]=graph.edge[minPoint][i];
}
}
++count;
}
}
//输入图的信息
void input()
{
int temp;

cout<<"下面输入顶点的信息"<<endl;
for(int i=1;i<=graph.num;i++)
{
cout<<"输入第"<<i<<"个顶点:";
cin>>graph.vertics[i];
}
for(int i=1;i<=graph.num;i++)
{
for(int j=i+1;j<=graph.num;j++)
{
cout<<"输入"<<graph.vertics[i]<<"到"<<graph.vertics[j]<<"的距离"<<endl;
cin>>temp;
graph.edge[i][j]=temp;
graph.edge[j][i]=temp;
}
}
}
//输出最小生成树的信息
void display()
{
for(int i=1;i<=graph.num;i++)
{
cout<<"第"<<i<<"个加入的顶点为:"<<tree[i].vertics<<",其花费权值:"<<tree[i].weight<<endl;
}
}
};

void main()
{
Prim test(7);//图有7个顶点
test.input();//输入图的信息
test.prim();//普里姆算法求最小生成树
test.display();//打印生成树的信息
}

//测试用图的邻接矩阵如下:
//      a   b   c   d   e
//  a      50  60 1000 1000
//  b          1000 65  40
//  c              52  1000
// d                   50
// e


 

这里用了简单的lowCost[]数组记录当前可选顶点到生成树上的最小距离,更好的办法是利用最小堆
不断的取当前与生成树具有最小距离的顶点加入到生成树中,更新堆中的每个顶点对应的距离,从而免去了每次都用O(n)时间
从lowcost[]中取最小距离顶点的时间和判断顶点是否已经加入生成树的if语句

 

与克鲁斯卡尔算法的共同点与区别:

 

1.都运用了贪心策略, 普里姆每次取与生成树部分距离最小的顶点加入到生成树中,并做加入记录(lowCost=-1),更新其他顶点到生成树的距离.

  克鲁斯卡尔算法则每次选择一个权值最小的边,判断边的两个顶点是否位于同一个连通分量内,如果在同一分量内,那么会成环,不满足最小生成树,所以这条边被抛弃(我的代码里简单的设边权为MAX来做记录,最好另外设置一个标记数组,这样程序可以重复使用)。

如果位于不同连通分量,那么这条边应该加入生成树内,并且把两个顶点所属的连通分量所属集合set进行重新赋值,使他们属于同一个连通分量。 可以容易的相出,如果两个顶点属于不同的连通分量,则他们的set值一定不同,如果属于同一连通分量,则一定相同。 这样的算法思路,成功n-1次时,则形成了n-1条边,最小生成树则形成。

 

2.区别在于一个以一个顶点为源点开始,贪心选择顶点加入到生成树中,共n次 。  一个不断选择最小边加入到生成树中,加入n-1次。

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息