实验1:最小生成树的构造

**

最小生成树的构造(Prime、Kruskal)

**
图解:
在这里插入图片描述
***Prim算法***又称为加边法,即每次选择最小权值的边加入到生成树中,然后再更新权值,如此反复,保证每次最优来达到最优解。
Kruskal算法:
1.对边按权重排序为e1、e2、…
2.若已选择V-1条边,停止。否则,按边的权重排序选择下一条边。
3.判断选择的边的两点是否在同一连通分支。若不在同一分支,则选择该边。返回步骤2。

以下是Prime算法的代码:

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

//首先是使用邻接矩阵完成Prim算法
struct Graph {
	int vexnum;  //顶点个数
	int edge;   //边的条数
	int ** arc; //邻接矩阵
	string *information; //记录每个顶点名称
};

//创建图
void createGraph(Graph & g) {
	cout << "请输入顶点数:输入边的条数" << endl;
	cin >> g.vexnum;
	cin >> g.edge;  //输入边的条数

	g.information = new string[g.vexnum];
	g.arc = new int*[g.vexnum];
	int i = 0;

	//开辟空间的同时,进行名称的初始化
	for (i = 0; i < g.vexnum; i++) {
		g.arc[i] = new int[g.vexnum];
		g.information[i] = "v" + std::to_string(i + 1);//对每个顶点进行命名
		for (int k = 0; k < g.vexnum; k++) {
			g.arc[i][k] = INT_MAX;          //初始化我们的邻接矩阵
		}
	}

	cout << "请输入每条边之间的顶点编号(顶点编号从1开始),以及该边的权重:" << endl;
	for (i = 0; i < g.edge; i++) {
		int start;
		int end;
		cin >> start;   //输入每条边的起点
		cin >> end;     //输入每条边的终点
		int weight;
		cin >> weight;
		g.arc[start - 1][end - 1] = weight;//无向图的边是相反的
		g.arc[end - 1][start - 1] = weight;
	}
}


//作为记录边的信息,这些边都是达到end的所有边中,权重最小的那个
struct Assis_array {
	int start; //边的终点
	int end;  //边的起点
	int weight;  //边的权重
};
//进行prim算法实现,使用的邻接矩阵的方法实现。
void Prim(Graph g, int begin) {

	//close_edge这个数组记录到达某个顶点的各个边中的权重最大的那个边
	Assis_array *close_edge = new Assis_array[g.vexnum];

	int j;
	cout << "图的最小生成树为:" << endl;
	//进行close_edge的初始化,更加开始起点进行初始化
	for (j = 0; j < g.vexnum; j++) {
		if (j != begin - 1) {
			close_edge[j].start = begin - 1;
			close_edge[j].end = j;
			close_edge[j].weight = g.arc[begin - 1][j];
		}
	}
	//把起点的close_edge中的值设置为-1,代表已经加入到集合U了
	close_edge[begin - 1].weight = -1;
	//访问剩下的顶点,并加入依次加入到集合U
	for (j = 1; j < g.vexnum; j++) {

		int min = INT_MAX;
		int k;
		int index;
		//寻找数组close_edge中权重最小的那个边
		for (k = 0; k < g.vexnum; k++) {
			if (close_edge[k].weight != -1) {
				if (close_edge[k].weight < min) {
					min = close_edge[k].weight;
					index = k;
				}
			}
		}
		//将权重最小的那条边的终点也加入到集合U
		close_edge[index].weight = -1;
		//输出对应的边的信息
		
		cout << g.information[close_edge[index].start]
			<< "-----"
			<< g.information[close_edge[index].end]
			<< "="
			<< g.arc[close_edge[index].start][close_edge[index].end]
			<< endl;

		//更新我们的close_edge数组。
		for (k = 0; k < g.vexnum; k++) {
			if (g.arc[close_edge[index].end][k] < close_edge[k].weight) {
				close_edge[k].weight = g.arc[close_edge[index].end][k];
				close_edge[k].start = close_edge[index].end;
				close_edge[k].end = k;
			}
		}
	}
}
int main()
{
	Graph g;
	createGraph(g);
	Prim(g, 1);
	system("pause");
	return 0;
}

以下是Kruskal算法的代码:

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


//边集结构,用于保存每条边的信息
typedef struct edge_tag {
	bool visit; //判断这条边是否加入到了最小生成树中
	int start;   //该边的起点
	int end;   //该边的终点
	int weight; //该边的权重
}Edge;

//创建一个图,但是图是使用边集结构来保存
void createGraph(Edge * &e, int Vexnum, int edge) {
	e = new Edge[edge];//为每条边集开辟空间
	int start = 0;
	int end = 0;
	int weight = 0;

	int i = 0;
	cout << "输入每条边的起点、终点和权重:" << endl;
	while (i != edge)
	{
		cin >> start >> end >> weight;
		e[i].start = start;
		e[i].end = end;
		e[i].weight = weight;
		e[i].visit = false; //每条边都还没被初始化
		++i;

	}
}

//我们需要对边集进行排序,排序是按照每条边的权重,从小到大排序。
int cmp(const void*  first, const void * second) {
	return ((Edge *)first)->weight - ((Edge *)second)->weight;
}

//其中parent代表顶点所在子树的根结点
//child代表每个顶点孩子结点的个数
int find_root(int child, int * parent) {

	//此时已经找到了该顶点所在树的根节点了
	if (parent[child] == child) {
		return child;
	}
	//往前递归,寻找它父亲的所在子树的根结点
	parent[child] = find_root(parent[child], parent);
	return parent[child];
}

//合并两个子树
bool union_tree(Edge  e, int * parent, int * child) {
	//先找出改边所在子树的根节点
	int root1;
	int root2;
	//记住我们顶点从1开始的,所以要减1
	root1 = find_root(e.start - 1, parent);
	root2 = find_root(e.end - 1, parent);
	//只有两个顶点不在同一颗子树上,才可以把两棵树并未一颗树
	if (root1 != root2) {
		//小树合并到大树中,看他们的孩子个数
		if (child[root1] > child[root2]) {
			parent[root2] = root1;
			//大树的孩子数量是小树的孩子数量加上
			//大树的孩子数量在加上小树根节点自己
			child[root1] += child[root2] + 1;
		}
		else {
			parent[root1] = root2;
			child[root2] += child[root1] + 1;
		}
		return true;
	}
	return false;
}

//克鲁斯卡算法的实现
void Kruskal() {
	int Vexnum = 0;
	int edge = 0;
	cout << "请输入图的顶点数和边数:" << endl;
	cin >> Vexnum >> edge;
	//声明一个边集数组
	Edge * edge_tag;
	//输入每条边的信息
	createGraph(edge_tag, Vexnum, edge);

	int * parent = new int[Vexnum]; //记录每个顶点所在子树的根节点下标
	int * child = new int[Vexnum]; //记录每个顶点为根节点时,其有的孩子节点的个数
	int i;
	for (i = 0; i < Vexnum; i++) {
		parent[i] = i;
		child[i] = 0;
	}
	//对边集数组进行排序,按照权重从小到达排序
	qsort(edge_tag, edge, sizeof(Edge), cmp);
	int count_vex; //记录输出的边的条数

	count_vex = i = 0;
	while (i != edge) {
		//如果两颗树可以组合在一起,说明该边是生成树的一条边
		if (union_tree(edge_tag[i], parent, child)) {
			cout << ("v" + std::to_string(edge_tag[i].start))
				<< "-----"
				<< ("v" + std::to_string(edge_tag[i].end))
				<< "="
				<< edge_tag[i].weight
				<< endl;
			edge_tag[i].visit = true;
			++count_vex; //生成树的边加1
		}
		//这里表示所有的边都已经加入成功
		if (count_vex == Vexnum - 1) {
			break;
		}
		++i;
	}

	if (count_vex != Vexnum - 1) {
		cout << "此图为非连通图!无法构成最小生成树。" << endl;
	}
	delete[] edge_tag;
	delete[] parent;
	delete[] child;
}

int main() {
	Kruskal();
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值