数据结构 Prim and Kruskal 算法

题目描述

根据输入创建无向网。分别用Prim算法和Kruskal算法构建最小生成树。(假设:输入数据的最小生成树唯一)

输入

顶点数n

n个顶点

边数m

m条边信息,格式为:顶点1 顶点2 权值

Prim算法的起点v

输出

输出最小生成树的权值之和

对两种算法,按树的生长顺序,输出边信息(Kruskal中边顶点按数组序号升序输出)

输入样例                                            输出样例

v1 v2 v3 v4 v5 v6                                                     15
10                                                                             prim:
v1 v2 6                                                                      v1 v3 1
v1 v3 1                                                                      v3 v6 4
v1 v4 5                                                                      v6 v4 2
v2 v3 5                                                                      v3 v2 5
v2 v5 3                                                                      v2 v5 3
v3 v4 5                                                                      kruskal:
v3 v5 6                                                                      v1 v3 1
v3 v6 4                                                                      v4 v6 2
v4 v6 2                                                                      v2 v5 3
v5 v6 6                                                                      v3 v6 4
v1                                                                              v2 v3 5

                                                                                  6

思考

在做题中,会遇到一些问题。比如在图的邻接矩阵中,0表示的是两个顶点间没有连接,而在Prim算法的lowcost数组中,由于无穷大在C/C++中不好表示,因此用0来表示,此处的0代表的是无穷大(其实也是没有连接的意思),所以要新增一个访问数组visit。

Prim算法实现

void Prim(int start) {
		//数组shortEdge
		string* adjvex = new string[n];//结点
		int* lowcost = new int[n];//最小权值
		//存储数组
		string* a1 = new string[n];//存储第一个结点
		string* a2 = new string[n];//存储第二个结点
		int* a3 = new int[n];//存储第一个顶点和第二个顶点间的权值

		int count = 0;//计数
		int k = 0;//存储数组下标
		int m = n - 1;

		//初始化
		for (int i = 0; i < n; i++) {
			adjvex[i] = a[start];
			lowcost[i] = matrix[start][i];
		}
		visit[start] = 1;//访问初始顶点
		lowcost[start] = 0;//初始顶点的lowcost值赋0

		//循环,不断更新存储值和shortEdge数组值
		while (m--) {

			int min = 99999;//最小权值数值标记
			int flag = 0;//最小权值下标标记

			//寻找最小权值
			for (int i = 0; i < n; i++) {
				//条件就是s2[i]不能等于0且小于min
				if (lowcost[i] != 0 && lowcost[i] < min) {
					min = lowcost[i];
					flag = i;
				}
			}

			//更新存储值
			visit[flag] = 1;//visit设置成已访问结点
			a1[k] = adjvex[flag];//第一个存储结点存储寻找到的下标对应的s1的值
			a2[k] = a[flag];//第二个存储节点存储寻找到的下表对应的a的值(邻接矩阵结点)
			a3[k] = min;//存储两者最小权值
			count += min;
			lowcost[flag] = 0;
			k++;

			//更新shortEdge数组
			for (int i = 0; i < n; i++) {
				//条件1:邻接矩阵的值小于lostcost数组的值 且 邻接矩阵的值不等于0
				//条件2:lostcost数组的值等于0 且 邻接矩阵的值不等于0
				//条件1,2的必要条件:visit数组的值等于0(还未被访问的顶点)
				if ((matrix[flag][i] < lowcost[i] && matrix[flag][i] != 0) || (lowcost[i] == 0 && matrix[flag][i] != 0)) {
					if (visit[i] == 0) {
						//lostcost的值替换成邻接矩阵的值
						//adjvex的值替换成邻接矩阵新顶点的值
						lowcost[i] = matrix[flag][i];
						adjvex[i] = a[flag];
					}
				}
			}
		}

		//输出
		cout << count << endl;
		cout << "prim:" << endl;
		for (int i = 0; i < k; i++) {
			cout << a1[i] << " " << a2[i] << " " << a3[i] << endl;
		}
	}

Kruskal算法实现

void Kruskal() {
		cout << "kruskal:" << endl;

		//用parent数组来表示结点的双亲结点
		//如结点4和结点1相连,则parent[4]=1
		//parent数组都初始化为-1,当为-1时,表示是根节点
		int* parent = new int[n];
		for (int i = 0; i < n; i++) {
			parent[i] = -1;
		}

		//把相连的结点及其权值放入数组中,为后面排序做准备
		string a1[100];//用来存放第1个结点
		string a2[100];//用来存放第2个结点
		int a3[100];//用来存放第1,2个结点间的权值
		int k = 0;//数组下标
		//只需要遍历邻接矩阵的上三角即可
		for (int j = 0; j < n; j++) {
			for (int i = 0; i < j; i++) {
				if (matrix[i][j] != 0) {
					//序号小的结点放在第1个结点存储数组(前面)
					//序号大的结点放在第2个结点存储数组(后面)
					if (find(a1[k]) > find(a2[k])) {
						a1[k] = a[j];
						a2[k] = a[i];
					}
					else {
						a1[k] = a[i];
						a2[k] = a[j];
					}
					a3[k] = matrix[i][j];
					k++;
				}
			}
		}

		//排序:把结点间的权值按照从大到小的顺序排列
		int temp1;
		string temp2;
		for (int i = 0; i < k - 1; i++) {
			for (int j = i + 1; j < k; j++) {
				if (a3[i] > a3[j]) {
					//交换,小的放在数组前面
					//注意:不仅仅交换的是权值,也要交换结点值
					temp1 = a3[i];
					a3[i] = a3[j];
					a3[j] = temp1;

					temp2 = a1[i];
					a1[i] = a1[j];
					a1[j] = temp2;

					temp2 = a2[i];
					a2[i] = a2[j];
					a2[j] = temp2;
				}
			}
		}

		int num = 0;//当num=n-1时,循环跳出
		for (int i = 0; i < k; i++) {
			//分别找出两个比较结点的双亲是否相同(是否在同一个连通分量中)
			string i1 = findRoot(parent, a1[i]);
			string i2 = findRoot(parent, a2[i]);
			//若不同,则相连(放入同一连通分量中)
			if (i1 != i2) {
				parent[find(i2)] = find(i1);//设置结点的双亲(连通分量)
				cout << a1[i] << " " << a2[i] << " " << a3[i] << endl;
				num++;
				if (num == n - 1) {
					break;
				}
			}
		}
	}
	string findRoot(int parent[], string v) {
		//这是一个判断结点的双亲的函数(根节点)
		int t = find(v);
		while (parent[t] > -1) {
			t = parent[t];
		}
		return a[t];
	}

完整代码

#include <iostream>
using namespace std;
class Map {
private:
	int n;
	string* a;
	int** matrix;
	int* visit;
public:
	void set() {

		//输入
		cin >> this->n;
		a = new string[n];
		for (int i = 0; i < n; i++) {
			cin >> a[i];
		}

		//初始化矩阵
		matrix = new int* [n];
		for (int i = 0; i < n; i++) {
			matrix[i] = new int[n];
		}
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				matrix[i][j] = 0;
			}
		}

		//初始化visit数组
		visit = new int[n];
		for (int i = 0; i < n; i++) {
			visit[i] = false;
		}

		//输入,构造邻接矩阵
		int t;
		cin >> t;
		while (t--) {
			string a;
			string c;
			int wei;
			cin >> a >> c >> wei;
			insert(a, c, wei);
		}
		string num;
		cin >> num;
		int i = find(num);
		//Prim算法
		Prim(i);
		//Kruskal算法
		Kruskal();
	}
	void insert(string a, string c, int wei) {
		int finda = find(a);
		int findc = find(c);
		matrix[finda][findc] = wei;
		matrix[findc][finda] = wei;

	}
	int find(string num) {
		for (int i = 0; i < n; i++) {
			if (a[i] == num) {
				return i;
			}
		}
	}
	void Prim(int start) {
		//数组shortEdge
		string* adjvex = new string[n];//结点
		int* lowcost = new int[n];//最小权值
		//存储数组
		string* a1 = new string[n];//存储第一个结点
		string* a2 = new string[n];//存储第二个结点
		int* a3 = new int[n];//存储第一个顶点和第二个顶点间的权值

		int count = 0;//计数
		int k = 0;//存储数组下标
		int m = n - 1;

		//初始化
		for (int i = 0; i < n; i++) {
			adjvex[i] = a[start];
			lowcost[i] = matrix[start][i];
		}
		visit[start] = 1;//访问初始顶点
		lowcost[start] = 0;//初始顶点的lowcost值赋0

		//循环,不断更新存储值和shortEdge数组值
		while (m--) {

			int min = 99999;//最小权值数值标记
			int flag = 0;//最小权值下标标记

			//寻找最小权值
			for (int i = 0; i < n; i++) {
				//条件就是s2[i]不能等于0且小于min
				if (lowcost[i] != 0 && lowcost[i] < min) {
					min = lowcost[i];
					flag = i;
				}
			}

			//更新存储值
			visit[flag] = 1;//visit设置成已访问结点
			a1[k] = adjvex[flag];//第一个存储结点存储寻找到的下标对应的s1的值
			a2[k] = a[flag];//第二个存储节点存储寻找到的下表对应的a的值(邻接矩阵结点)
			a3[k] = min;//存储两者最小权值
			count += min;
			lowcost[flag] = 0;
			k++;

			//更新shortEdge数组
			for (int i = 0; i < n; i++) {
				//条件1:邻接矩阵的值小于lostcost数组的值 且 邻接矩阵的值不等于0
				//条件2:lostcost数组的值等于0 且 邻接矩阵的值不等于0
				//条件1,2的必要条件:visit数组的值等于0(还未被访问的顶点)
				if ((matrix[flag][i] < lowcost[i] && matrix[flag][i] != 0) || (lowcost[i] == 0 && matrix[flag][i] != 0)) {
					if (visit[i] == 0) {
						//lostcost的值替换成邻接矩阵的值
						//adjvex的值替换成邻接矩阵新顶点的值
						lowcost[i] = matrix[flag][i];
						adjvex[i] = a[flag];
					}
				}
			}
		}

		//输出
		cout << count << endl;
		cout << "prim:" << endl;
		for (int i = 0; i < k; i++) {
			cout << a1[i] << " " << a2[i] << " " << a3[i] << endl;
		}
	}
	void Kruskal() {
		cout << "kruskal:" << endl;

		//用parent数组来表示结点的双亲结点
		//如结点4和结点1相连,则parent[4]=1
		//parent数组都初始化为-1,当为-1时,表示是根节点
		int* parent = new int[n];
		for (int i = 0; i < n; i++) {
			parent[i] = -1;
		}

		//把相连的结点及其权值放入数组中,为后面排序做准备
		string a1[100];//用来存放第1个结点
		string a2[100];//用来存放第2个结点
		int a3[100];//用来存放第1,2个结点间的权值
		int k = 0;//数组下标
		//只需要遍历邻接矩阵的上三角即可
		for (int j = 0; j < n; j++) {
			for (int i = 0; i < j; i++) {
				if (matrix[i][j] != 0) {
					//序号小的结点放在第1个结点存储数组(前面)
					//序号大的结点放在第2个结点存储数组(后面)
					if (find(a1[k]) > find(a2[k])) {
						a1[k] = a[j];
						a2[k] = a[i];
					}
					else {
						a1[k] = a[i];
						a2[k] = a[j];
					}
					a3[k] = matrix[i][j];
					k++;
				}
			}
		}

		//排序:把结点间的权值按照从大到小的顺序排列
		int temp1;
		string temp2;
		for (int i = 0; i < k - 1; i++) {
			for (int j = i + 1; j < k; j++) {
				if (a3[i] > a3[j]) {
					//交换,小的放在数组前面
					//注意:不仅仅交换的是权值,也要交换结点值
					temp1 = a3[i];
					a3[i] = a3[j];
					a3[j] = temp1;

					temp2 = a1[i];
					a1[i] = a1[j];
					a1[j] = temp2;

					temp2 = a2[i];
					a2[i] = a2[j];
					a2[j] = temp2;
				}
			}
		}

		int num = 0;//当num=n-1时,循环跳出
		for (int i = 0; i < k; i++) {
			//分别找出两个比较结点的双亲是否相同(是否在同一个连通分量中)
			string i1 = findRoot(parent, a1[i]);
			string i2 = findRoot(parent, a2[i]);
			//若不同,则相连(放入同一连通分量中)
			if (i1 != i2) {
				parent[find(i2)] = find(i1);//设置结点的双亲(连通分量)
				cout << a1[i] << " " << a2[i] << " " << a3[i] << endl;
				num++;
				if (num == n - 1) {
					break;
				}
			}
		}
	}
	string findRoot(int parent[], string v) {
		//这是一个判断结点的双亲的函数(根节点)
		int t = find(v);
		while (parent[t] > -1) {
			t = parent[t];
		}
		return a[t];
	}
};
int main()
{
	Map map;
	map.set();
	return 0;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值