最小生成树的Prim和Kruskal的c++语言实现-(北邮算法与分析作业题)

1.题目:
最小生成树
从昆明LTE网络中,选取部分基站,计算基站间的距离,在部分基站间引入边,得到
1)22个基站顶点组成的图
2)42个基站顶点组成的图
最小生成树
生成这2个图的最小生成树
要求:
采用K算法,或P算法;
给出最小生成树的成本/代价/耗费cost
做图,呈现出最小生成树,
      可以在原图上,用红色、粗线条,标记最小生成树的边
----------------------------------------------------------------
2.Prim 算法
     1)  算法思想:采用集合存储法,由于Prim算法是对点的选择,从某一个点开始,选择
    到该点的最小边所对应的定点,加入到确定集合中,然后再在确定集合里找到非确定
   点集合的最小边,再加入到确定点集合里依次类推,直到所有的点都加入到确定点集合中,*/
     2)算法代码:
#include<iostream>
#include<fstream>
#define BASE_NUM1 22
#define BASE_NUM2 42
#define INIF 999999999 
struct BRANCH{
	int x;
	int y;
	double weight;
};
int ID[BASE_NUM2+1];//基站编号
BRANCH branch[BASE_NUM2]; //记录最小生成树有哪些边加入其中了,共有n-1条边加入其中 
int book[BASE_NUM2+1];//标记数组,=0表示未加入确定集,=1表示加入确定集
double graph[BASE_NUM2+1][BASE_NUM2+1]; //存储地图
double min_weight;  //生成树的最小权值 
void Prim(int base_num);
void Out_Min_Tree(int base_num);
using namespace std;
int main()
{
	system("color 0B");
	fstream file;
		/******对文件一的处理**********************/ 
	file.open("图22.txt",ios::in);
	if(!file)
	{
		cout<<"图22打开失败!";exit(1);
	} 
		int i,j;
	double temp;
	//写入数据 
	for(i=1;i<=BASE_NUM1;i++)
	  file>>ID[i];
	for(i=1;i<=BASE_NUM1;i++)
	{
		file>>temp;
		for(j=1;j<=BASE_NUM1;j++)
		{
			file>>temp;
			  graph[i][j]=(temp<0.0)?INIF:temp;
		} 
	}
	//Prim算法求最小生成树
	Prim(BASE_NUM1);
	cout<<"(Prim算法)图一最小生成树所包含的边为:"<<endl;	
	Out_Min_Tree(BASE_NUM1);  //输出最小生成树 
	cout<<"最小生成树的成本="<<min_weight<<endl;
	file.close(); 
	/******对文件二的处理**********************/
	min_weight=0; 
	file.open("图42.txt",ios::in);
	if(!file)
	{
		cout<<"图42打开失败!";exit(1);
	} 
	//写入数据 
	for(i=1;i<=BASE_NUM2;i++)
	  file>>ID[i];
	for(i=1;i<=BASE_NUM2;i++)
	{
		file>>temp;
		for(j=1;j<=BASE_NUM2;j++)
		{
			file>>temp;
			  graph[i][j]=(temp<0.0)?INIF:temp;
		} 
	}
	//Prim算法求最小生成树
	Prim(BASE_NUM2);
	cout<<"(Prim算法)图二最小生成树所包含的边为:"<<endl;	
	Out_Min_Tree(BASE_NUM2);  //输出最小生成树 
	cout<<"最小生成树的成本="<<min_weight<<endl; 
	return 0;
 } 
 void Prim(int base_num)
 {
 	int have[base_num+1],i,j,k; //已经加入的集合
 	double min_edge;
	 int start,end;
	 //初始化
	 for(i=1;i<=base_num;i++)
	     book[i]=0; 
	book[1]=1;//从编号为1的顶点开始构造
	have[1]=1;  //第一个定点入确定集合集合 
	for(i=1;i<base_num;i++)
	{
		min_edge=INIF;
		for(j=1;j<=i;j++)  //在确定集里找 
		{
			for(k=1;k<=base_num;k++)
			{
				if(book[k]==0&&min_edge>graph[have[j]][k])
				{
					start=have[j];
					end=k;
					min_edge=graph[start][end];
				}
			}
		 }
		branch[i].x=start;
		branch[i].y=end;
		branch[i].weight=min_edge;
		min_weight+=min_edge; 
		book[end]=1;
		have[i+1]=end; 	
	  }  
 }
 
void Out_Min_Tree(int base_num)
{
	int i;
	cout<<"起点位置:\t"<<"终点位置:\t"<<"边权\t"<<endl; 
	for(i=1;i<base_num;i++)
	{
		cout<<branch[i].x<<"\t\t"<<branch[i].y<<"\t\t"<<branch[i].weight<<endl;
		
	}
 } 

---------------------------------------------------------------------
2.Kruskal算法实现
  1)算法思想:由K算法的出发点可知,第一:对边实现最小性选择,第二,不能成环。
对于第一点:将边的信息按权值从小到大排序,再进行选择;对于第二点:采用并查集思想,一旦一条边能够加入加入到某一个集合内,将该集合的边的所有顶点的标记置成相同的值,表示一类,直到选择了n-1条边为止。

2)代码实现
#include<iostream>
#include<fstream>
#include<algorithm>
#include<math.h>
#define BASE_NUM1 22
#define BASE_NUM2 42
#define INIF 999999999 
#define MAX_EDGE 10000  
using namespace std;
/*并查集方法,将边*/ 
int ID[BASE_NUM2+1];//基站编号
double graph[BASE_NUM2+1][BASE_NUM2+1]; //存储地图
double min_weight;  //生成树的最小权值 
struct EDGE{
	int start;
	int end;
	double weight;
}; 
EDGE edge[MAX_EDGE];  //存储原图中的边的集合 
EDGE branch[BASE_NUM2+1]; //存储选定的树中的边 
void Kruskal(int base_num,int edge_num);
void Out_Min_Tree(int base_num);
 
int cmp(EDGE&a, EDGE&b)
{
	return a.weight<b.weight;
 } 
int main()
{
	system("color 0B");
	fstream file;
	/******对文件一的处理**********************/ 
	file.open("图22.txt",ios::in);
	if(!file)
	{
		cout<<"图22打开失败!";exit(1);
	} 
		int i,j,sum_edge=0;
		double temp;
	for(i=1;i<=BASE_NUM1;i++)
	  file>>ID[i];
	for(i=1;i<=BASE_NUM1;i++)
	{
		file>>temp;
		for(j=1;j<=BASE_NUM1;j++)
		{
			file>>temp;
			  graph[i][j]=(temp<0.0)?INIF:temp;
			  if(i<j&&temp>0)
			  {
			     edge[sum_edge].start=i;
			     edge[sum_edge].end=j;
			     edge[sum_edge].weight=temp;
			     sum_edge++;
			  }
		} 
	}
	 //对边的集合进行从小到大排序
	 sort(edge,edge+sum_edge,cmp);
	Kruskal(BASE_NUM1,sum_edge);
	cout<<"(Kruskal算法)图一最小生成树所包含的边为:"<<endl;	
	Out_Min_Tree(BASE_NUM1);  //输出最小生成树  
	cout<<"最小生成树的成本="<<min_weight<<endl;
	file.close(); 
	/******对文件二的处理**********************/ 
	sum_edge=0;
	min_weight=0;
		file.open("图42.txt",ios::in);
	if(!file)
	{
		cout<<"图42打开失败!";exit(1);
	} 
	for(i=1;i<=BASE_NUM2;i++)
	  file>>ID[i];
	for(i=1;i<=BASE_NUM2;i++)
	{
		file>>temp;
		for(j=1;j<=BASE_NUM2;j++)
		{
			file>>temp;
			  graph[i][j]=(temp<0.0)?INIF:temp;
			  if(i<j&&temp>0)
			  {
			     edge[sum_edge].start=i;
			     edge[sum_edge].end=j;
			     edge[sum_edge].weight=temp;
			     sum_edge++;
			  }
		} 
	}
	 //对边的集合进行从小到大排序
	 sort(edge,edge+sum_edge,cmp);
	Kruskal(BASE_NUM2,sum_edge);
	cout<<"(Kruskal算法)图一最小生成树所包含的边为:"<<endl;	
	Out_Min_Tree(BASE_NUM2);  //输出最小生成树  
	cout<<"最小生成树的成本="<<min_weight<<endl;
	 return 0; 
}
void Kruskal(int base_num,int edge_num)
{
	int i,j,m1,m2,sn1,sn2,count;
	int v_set[base_num+1];
	//初始化铺助数组
	for(i=1;i<=base_num;i++)
	   v_set[i]=i;
	count=1;//count表示当前构造最小生成树的第几条边
	j=0;  //边集的下标
	while(count<base_num)
	{
		m1=edge[j].start;sn1=v_set[m1];
		m2=edge[j].end;sn2=v_set[m2];
		if(sn1!=sn2)  //加入生成树边的集合中   关键条件,保证不会形成一个环 
		{
			branch[count]=edge[j];
			min_weight+=branch[count].weight; 
			count++;
			for(i=1;i<=base_num;i++)
			  if(v_set[i]==sn2)    //集合编号为sn2的改成sn1
			     v_set[i]=sn1; 
		}
		j++;  //扫描下一条边 
	}    
}
void Out_Min_Tree(int base_num)
{
	int i;
	cout<<"起点位置:\t"<<"终点位置:\t"<<"边权\t"<<endl; 
	for(i=1;i<base_num;i++)
	{
		cout<<branch[i].start<<"\t\t"<<branch[i].end<<"\t\t"<<branch[i].weight<<endl;
		
	}
 } 

猜你喜欢

转载自blog.csdn.net/liangwgl/article/details/80706460