再写——Prim && Kruskal

重新写了最小生成树的两个经典算法—— prim 和 kruskal算法,之前写的Prim算法,是比较直接的 就是先让边的集合为空,然后在扫描最小边的过程中,也是根据该算法的定义来写的 是比较直接的写法 看这里 就是之前的Prim算法实现 ,这次实现的,个人比较喜欢。

思想是这样的:

(1)先在最小树的顶点集U={v},以和v相邻的所有边为候选边;

(2)重复以下步骤n-1次,使得其他n-1个顶点加入到U中:

1、从候选边中选出一条权值最小的,设该边在V-U中的顶点是vk,将vk加入U中,删除和vk相邻的边(程序中以一个标志来表示)

2、考察当前V-U中所有顶点vj,修改候选边:若(vk,vj)的权值小于原来和vj关联的候选变,就用后者作为候选边。

下面是代码:

//prim.h

#include <iostream>
#include <string>
#include <queue>
using namespace std;

const int MAX_VEX_NUM=20;
const int MAX_NUM=32767;
bool visited[20]; 

struct Tree_Edge
{
	int star;
	int end;
	int weight;
};

class MGraph
{
private:
	string vexs[MAX_VEX_NUM];
	int arcs[MAX_VEX_NUM][MAX_VEX_NUM];
	int vexnum;
	int arcnum;
public:
	int Get_VexNum()
	{
		return vexnum;
	}

	int Locate_Vex(string v)
	{
		for(int i=0;i<vexnum;i++)
			if(vexs[i] == v)
				return i;
		return -1;
	}

	void Create_MG()
	{
		//构造无向图的邻接矩阵
		int i,j,k;
		cout<<"输入图的顶点数目(少于20): ";
		cin>>vexnum;

		while(vexnum>20)
		{
			cout<<"输入的顶点数超过20,请重新输入:";
			cin>>vexnum;
		}

		cout<<"输入各个顶点名称:";
		for(i=0;i<vexnum;i++)
			cin>>vexs[i];

		for(i=0;i<vexnum;i++)
			for(j=0;j<vexnum;j++)
			{
				arcs[i][j]=MAX_NUM;
			}
		
		cout<<"输入边的总数:";
		cin>>arcnum;

		string v1,v2;
		int w;
		for(k=0;k<arcnum;k++)
		{
			cout<<"输入每条边对应的两个顶点和该边对应的权值:";
			cin>>v1>>v2>>w;
			i=Locate_Vex(v1);
			j=Locate_Vex(v2);

			while(i == -1 || j == -1)
			{
				cout<<"输入的顶点中有不符合要求的,重新输入:";
				cin>>v1>>v2;
				i=Locate_Vex(v1);
				j=Locate_Vex(v2);
			}

			arcs[i][j]=w;
			arcs[j][i]=w;
		}
	
		cout<<"无向网构造完成"<<endl;
	}

	void DFS_Traverse()
	{
		for(int i=0;i<vexnum;i++)
			visited[i]=false;
		for(i=0;i<vexnum;i++)
			if(!visited[i])
				DFS(i);
	}

	void DFS(int v)
	{
		visited[v]=true;
		cout<<vexs[v]<<"  ";
		
		for(int j=0;j<vexnum;j++)
			if(arcs[v][j] != MAX_NUM && !visited[j])
				DFS(j);
	}

	void BFS_Traverse()
	{
		queue<int> q;
		int u,w,v;
		for(v=0;v<vexnum;v++)
			visited[v]=false;

		for(v=0;v<vexnum;v++)
		{
			if(!visited[v])
			{
				visited[v]=true;
				cout<<vexs[v]<<"  ";
				q.push(v);

				while(!q.empty())
				{
					u=q.front();
					q.pop();

					for(w=0;w<vexnum;w++)
					{
						if( (arcs[u][w] != MAX_NUM) && !visited[w])
						{
							visited[w]=true;
							cout<<vexs[w]<<"  ";
							q.push(w);
						}
					}
				}
			}
		}
	}

	void Prim_Min_Tree(int v,Tree_Edge c[])
	{
		int lowcast[MAX_NUM];
		int closet[MAX_NUM];
		int i,min,j,k;
	
		for(i=0;i<vexnum;i++)
		{
			lowcast[i]=arcs[v][i];
			closet[i]=v;
		}
	
		lowcast[v]=0;
		
		for(i=1;i<vexnum;i++)
		{
			min=MAX_NUM;
			for(j=0;j<vexnum;j++)
			{
				if(lowcast[j]!=0 && lowcast[j]<min)
				{
					min=lowcast[j];
					k=j;
				}
			}
			lowcast[k]=0; //标志该顶点已经是U中顶点
			
			c[i].star=closet[k];
			c[i].end=k;
			c[i].weight=min;

			//下面是修改lowcast和closet
			for(j=0;j<vexnum;j++)
			{
				if(arcs[k][j]<lowcast[j])
				{
					lowcast[j]=arcs[k][j];
					closet[j]=k;
				}
			}
		}

		cout<<"最小生成树 完成~~"<<endl;
	}

	void Print_Tree(Tree_Edge c[])
	{
		for(int i=1;i<vexnum;i++)
			cout<<vexs[c[i].star]<<"-->"<<vexs[c[i].end]<<" : "<<c[i].weight<<endl;
	}
};

#include "prim.h"
#include <iostream>
using namespace std;

int main()
{
	MGraph G;
	G.Create_MG();
	
	cout<<"图的深度优先遍历为:";
	G.DFS_Traverse();
	cout<<endl;
	
	cout<<"图的广度优先遍历为:";
	G.BFS_Traverse();
	cout<<endl;
	
	string v;
	int pos;
	cout<<"输入建立最小树的起点的名称:";
	cin>>v;
	pos=G.Locate_Vex(v);
	while(pos == -1)
	{
		cout<<"顶点不存在,重新输入:";
		cin>>v;
		pos=G.Locate_Vex(v);
	}
	
	Tree_Edge c[MAX_NUM];
	G.Prim_Min_Tree(pos,c);

	cout<<"最小树的边的组成如下所示:"<<endl;
	G.Print_Tree(c);

	return 0;
}

测试结果:

输入图的顶点数目(少于20): 6
输入各个顶点名称:v1 v2 v3 v4 v5 v6
输入边的总数:10
输入每条边对应的两个顶点和该边对应的权值:v1 v2 6
输入每条边对应的两个顶点和该边对应的权值:v1 v3 1
输入每条边对应的两个顶点和该边对应的权值:v1 v4 5
输入每条边对应的两个顶点和该边对应的权值:v2 v5 3
输入每条边对应的两个顶点和该边对应的权值:v2 v3 5
输入每条边对应的两个顶点和该边对应的权值:v3 v4 5
输入每条边对应的两个顶点和该边对应的权值:v3 v6 4
输入每条边对应的两个顶点和该边对应的权值:v4 v6 2
输入每条边对应的两个顶点和该边对应的权值:v3 v5 6
输入每条边对应的两个顶点和该边对应的权值:v5 v6 6
无向网构造完成
图的深度优先遍历为:v1  v2  v3  v4  v6  v5
图的广度优先遍历为:v1  v2  v3  v4  v5  v6
输入建立最小树的起点的名称:v1
最小生成树 完成~~
最小树的边的组成如下所示:
v1-->v3 : 1
v3-->v6 : 4
v6-->v4 : 2
v3-->v2 : 5
v2-->v5 : 3
Press any key to continue


kruskal算法的一个中心思想就是在构造最小树的过程中,不可以使新边和原来的边构成回路,这次写的和之前写的有点不一样的就是,排序时,用了快排,在判断新边是否会和现在的边构成回路时,用了并查集。这个算法也是并查集的一个经典的运用,之前写的那个,用了并查集思想,但是这次是写了一个并查集然后用的,比较明显,看起来也比较清晰。之前写的kruskul算法实现

下面是代码:

//并查集头文件
#ifndef UFSET_H_
#define UFSET_H_

#include <iostream>
using namespace std;

const int MAX_SIZE=100;

int parent[MAX_SIZE];
int rank[MAX_SIZE];

int Find(int x)
{
	int y,root,w;
	y=x;
	while(parent[y]!=y)
	{
		y=parent[y];
	}
	root=y;
	y=x;
	while(parent[y]!=y) //执行路径压缩
	{
		w=parent[y];
		parent[y]=root;
		y=w;
	}
	return root;
}

void Union(int root1,int root2)
{
	int u,v;
	u=Find(root1);
	v=Find(root2);
	if(rank[u]<=rank[v])
	{
		parent[u]=v;
		if(rank[u]==rank[v])
			++rank[v];
	}
	else
		parent[v]=u;
}

void Init()
{
	memset(rank,0,sizeof(rank));
	for(int i=1;i<MAX_SIZE;i++)
		parent[i]=i;
}

#endif

//图的定义以及实现的头文件
#ifndef GRAPH_H_
#define GRAPH_H_

#include "ufset.h"
#include <iostream>
#include <string>
#include <queue>
using namespace std;

const int MAX_VEX_NUM=20;
const int MAX_NUM=32767;
bool visited[MAX_VEX_NUM];

struct Tree_Edge
{
	int star;
	int end;
	int weight;
}c[MAX_NUM];

void Quick_Sort(int low,int high);

class MGraph
{
private:
	string vexs[MAX_VEX_NUM];
	int arcs[MAX_VEX_NUM][MAX_VEX_NUM];
	int arcnum;
	int vexnum;
public:
	int Locate_Vex(string v)
	{
		for(int i=0;i<vexnum;i++)
			if(vexs[i] == v)
				return i;
		return -1;
	}

	void Create_Graph()
	{
		int i,j,v1,v2,w;
		string u,v;
		cout<<"输入定点数目(少于20):  ";
		cin>>vexnum;

		while(vexnum>20)
		{
			cout<<"请输入小于20的整数:";
			cin>>vexnum;
		}
		
		cout<<"输入顶点名称:";
		for(i=0;i<vexnum;i++)
			cin>>vexs[i];

		//初始化邻接矩阵
		for(i=0;i<vexnum;i++)
			for(j=0;j<vexnum;j++)
				arcs[i][j]=MAX_NUM;

		cout<<"输入边的总数:";
		cin>>arcnum;

		for(i=0;i<arcnum;i++)
		{
			cout<<"输入每一条边对应的两个顶点和该边权值:";
			cin>>u>>v>>w;

			v1=Locate_Vex(u);
			v2=Locate_Vex(v);
			while(v1 == -1 || v2 == -1)
			{
				cout<<"顶点中有不符合要求的,请重新输入:";
				cin>>u>>v;
				v1=Locate_Vex(u);
				v2=Locate_Vex(v);
			}
			
			arcs[v1][v2]=w;
			arcs[v2][v1]=w;
			c[i].star=v1;
			c[i].end=v2;
			c[i].weight=w;
		}

		cout<<"无向网构造完成"<<endl;
	}

	void DFS_Traverse()
	{
		for(int i=0;i<vexnum;i++)
			visited[i]=false;
		for(i=0;i<vexnum;i++)
			if(!visited[i])
				DFS(i);
	}

	void DFS(int v)
	{
		visited[v]=true;
		cout<<vexs[v]<<"  ";

		for(int i=0;i<vexnum;i++)
			if(arcs[v][i]<MAX_NUM && !visited[i])
				DFS(i);
	}

	void BFS_Traverse()
	{
		for(int i=0;i<vexnum;i++)
			visited[i]=false;
		for(i=0;i<vexnum;i++)
			if(!visited[i])
				BFS(i);
	}

	void BFS(int v)
	{
		visited[v]=true;
		cout<<vexs[v]<<"  ";

		queue<int> qu;
		int w;
		qu.push(v);

		while(!qu.empty())
		{
			w=qu.front();
			qu.pop();
			for(int i=0;i<vexnum;i++)
			{
				if(arcs[w][i]<MAX_NUM && !visited[i])
				{
					visited[i]=true;
					cout<<vexs[i]<<"  ";
					qu.push(i);
				}
			}
		}
	}

	void KrusKal_Min_Tree(int v,Tree_Edge c2[])
	{
		int i,j,k,u1,v1,sn1,sn2;
		Init(); //初始化并查集

		Quick_Sort(0,arcnum-1);

		k=1;
		j=0;
		while(k<vexnum)
		{
			u1=c[j].star;
			v1=c[j].end;
			sn1=Find(u1); //并查集中查找u1的父亲
			sn2=Find(v1);
			if(sn1 != sn2)
			{
				c2[k]=c[j];
				k++;
				Union(u1,v1); //合并两个集合
			}
			j++;
		}

		cout<<"最小树构造完成"<<endl;
	}

	void Print_Tree(Tree_Edge c2[])
	{
		for(int i=1;i<vexnum;i++)
			cout<<vexs[ c2[i].star ]<<"-->"<<vexs[ c2[i].end ]<<" : "<<c2[i].weight<<endl;
	}
};

void Quick_Sort(int low,int high)
{
	int i,j;
	Tree_Edge t;
	i=low;
	j=high;
	t=c[low];//把第一个元素作为基准
	while(i<j)
	{
		while((i<j)&&(c[j].weight >= t.weight))
			j--;
		if(i<j)
			c[i]=c[j];
		i++;
		while((i<j)&&(c[i].weight <= t.weight))
			i++;
		if(i<j)
			c[j]=c[i];
		j--;
	}
	c[i]=t;
	if(low<high)
	{
		Quick_Sort(low,i-1);//排列t的左边部分
		Quick_Sort(i+1,high);//排列右边部分
	}
}

#endif

#include "graph.h"
#include <iostream>
#include <string>
using namespace std;

int main()
{
	MGraph G;
	G.Create_Graph();
	
	cout<<"图的深度优先遍历为:";
	G.DFS_Traverse();
	cout<<endl;
	
	cout<<"图的广度优先遍历为:";
	G.BFS_Traverse();
	cout<<endl;
	
	string v;
	int pos;
	cout<<"输入建立最小树的起点的名称:";
	cin>>v;
	pos=G.Locate_Vex(v);
	while(pos == -1)
	{
		cout<<"顶点不存在,重新输入:";
		cin>>v;
		pos=G.Locate_Vex(v);
	}
	
	Tree_Edge Edges[MAX_NUM];
	G.KrusKal_Min_Tree(pos,Edges);
	
	cout<<"最小树的边的组成如下所示:"<<endl;
	G.Print_Tree(Edges);	

	return 0;
}

测试结果:

输入定点数目(少于20):  6
输入顶点名称:v1 v2 v3 v4 v5 v6
输入边的总数:10
输入每一条边对应的两个顶点和该边权值:v1 v2 6
输入每一条边对应的两个顶点和该边权值:v1 v3 1
输入每一条边对应的两个顶点和该边权值:v1 v4 5
输入每一条边对应的两个顶点和该边权值:v2 v3 5
输入每一条边对应的两个顶点和该边权值:v3 v4 5
输入每一条边对应的两个顶点和该边权值:v2 v5 3
输入每一条边对应的两个顶点和该边权值:v3 v5 6
输入每一条边对应的两个顶点和该边权值:v3 v6 4
输入每一条边对应的两个顶点和该边权值:v4 v6 2
输入每一条边对应的两个顶点和该边权值:v5 v6 6
无向网构造完成
图的深度优先遍历为:v1  v2  v3  v4  v6  v5
图的广度优先遍历为:v1  v2  v3  v4  v5  v6
输入建立最小树的起点的名称:v1
最小树构造完成
最小树的边的组成如下所示:
v1-->v3 : 1
v4-->v6 : 2
v2-->v5 : 3
v3-->v6 : 4
v2-->v3 : 5
Press any key to continue

下面是测试中生产的无向图 以及生产的最小树:


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值