【最小生成树】—— Kruskal算法(C++详解版) 思路分析 & 代码实现

思路分析

克鲁斯卡尔算法的基本思路,是在联通图已经建立的基础上,通过一次次地选择某条边来连接两个个顶点,直到所有顶点都被连接到一起为止, 因此又被称为”加点法“。

具体步骤如下:

1. 将图中的所有边按照权值从小到大进行排序。//本代码利用优先队列实现
2. 从权值最小的边开始,依次选择边,并检查是否会形成环路。//”检验是否形成回路“是代码实现的难点,下面会细讲~
3. 如果选中的边不会形成环路,则将其加入最小生成树中。//本代码用一个新的邻接矩阵存储
4. 继续选择下一条权值较小的边,直到所有顶点都被连接为止。

//ps:最早将所有边存于优先队列中,每条边只在上述过程走一次, 每取出一条边,就将其从队列中删除,当队列为空时,则理论上最小生成树也形成了(如果生成树可以形成的话)。

自定义结构体

typedef struct { // 定义结构体Node
	int u, v; // 边的起点和终点
	int wt; // 边的权值
}Node;

关于”判断是否形成环路“的阐释

在实现过程中,通常会使用并查集来判断是否形成环路。如果选中的边的两个顶点属于同一个集合,则说明会形成环路,此时应该舍弃该边。否则,将两个顶点合并到同一个集合中,并将该边加入最小生成树中。相关代码节选如下:

if (rem[t_node.u] != rem[t_node.v]) 
// 可以理解成rem用来存储上级,若rem值想等,则已经与同一结点连接,借此判断是否形成环
{
    //如果不成环,则加入,并更新rem
	for (int i = 1; i <= n; i++) // 更新并查集
	{
		rem[i] = rem[i] == rem[t_node.v] ? rem[t_node.u] : rem[i];
	}
}

建立最小生成树的代码如下:

int rem[size] = { 0 }; // 定义并初始化并查集数组rem
for (int i = 0; i < size; i++) rem[i] = i; // 初始化并查集
while (!pq.empty()) // 当优先队列不为空时
{
	t_node = pq.top(); pq.pop(); // 取出权值最小的边
	if (rem[t_node.u] != rem[t_node.v]) // 判断是否形成环
	{
		graph[t_node.u][t_node.v] = t_node.wt; // 更新邻接矩阵
		graph[t_node.v][t_node.u] = t_node.wt;
		for (int i = 1; i <= n; i++) // 更新并查集
		{
			rem[i] = rem[i] == rem[t_node.v] ? rem[t_node.u] : rem[i];
		}
	}
}

完整代码如下:

#include<bits/stdc++.h> // 包含标准库头文件
using namespace std; // 使用标准命名空间

#define max 1000000 // 定义最大值常量
#define size 100 // 定义数组大小常量
//set<int>s; // 注释掉的代码

typedef struct { // 定义结构体Node
	int u, v; // 边的起点和终点
	int wt; // 边的权值
}Node;

struct cmp { // 定义比较器结构体cmp
	bool operator()(const Node& a, const Node& b) // 重载()操作符
	{
		return a.wt > b.wt; // 按权值从小到大排序
	}
};

priority_queue<Node, vector<Node>, cmp>pq; // 定义优先队列pq

int main() // 主函数
{
	int n, m; // 定义顶点数和边数变量
	cin >> n >> m; // 输入顶点数和边数
	Node t_node; // 定义边的结构体变量t_node
	for (int i = 0; i < m; i++) // 循环读入边的起点、终点和权值
	{
		cin >> t_node.u >> t_node.v >> t_node.wt;
		pq.push(t_node); // 将边加入优先队列
	}
	int graph[size][size] = { 0 }; // 定义邻接矩阵graph并初始化为0
	for (int i = 0; i < size; i++) { // 初始化邻接矩阵为0
		for (int j = 0; j < size; j++) graph[i][j] = 0;
	}
	//kruskal
	int rem[size] = { 0 }; // 定义并初始化并查集数组rem
	for (int i = 0; i < size; i++) rem[i] = i; // 初始化并查集
	while (!pq.empty()) // 当优先队列不为空时
	{
		t_node = pq.top(); pq.pop(); // 取出权值最小的边
		if (rem[t_node.u] != rem[t_node.v]) // 判断是否形成环
		{
			graph[t_node.u][t_node.v] = t_node.wt; // 更新邻接矩阵
			graph[t_node.v][t_node.u] = t_node.wt;
			for (int i = 1; i <= n; i++) // 更新并查集
			{
				rem[i] = rem[i] == rem[t_node.v] ? rem[t_node.u] : rem[i];
			}
		}
	}
	//最小生成树以邻接矩阵的方式呈现
	for (int i = 1; i <= n; i++) // 遍历输出最小生成树的邻接矩阵
	{
		for (int j = 1; j <= n; j++)
		{
			cout << graph[i][j] << " "; // 输出邻接矩阵元素
		}
		cout << endl; // 换行
	}
}

~希望对你有帮助~

  • 28
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最小生成树Kruskal算法是一种常用的图算法,用于找到一个连通图的最小生成树。下面是Kruskal算法C++代码示例: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; // 定义边的结构体 struct Edge { int src, dest, weight; }; // 定义并查集的数据结构 classFind { public: vector<int> parent, rank; UnionFind(int n) { parent.resize(n); rank.resize(n); for (int i = 0; i < n; i++) { parent[i] = i; rank[i] = 0; } } int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); } return parent[x]; } void unionSet(int x, int y { int rootX = find(x); int rootY = find(y); if (rootX != rootY) { if (rank[rootX] < rank[rootY]) { parent[rootX] = rootY; } else if (rank[rootX] > rank[rootY]) { parent[rootY] = rootX; } else { parent[rootY] = rootX; rank[rootX]++; } } } }; // Kruskal算法实现 vector<Edge> kruskalMST(vector<Edge>& edges, int V) { // 按照边的权重进行排序 sort(edges.begin(), edges.end(), [](const Edge& a, const Edge& b) { return a.weight < b.weight; }); vector<Edge> result; UnionFind uf(V); for (const Edge& edge : edges) { int src = edge.src; int dest = edge.dest; // 判断加入边后是否形成环路 if (uf.find(src) != uf.find(dest)) { uf.unionSet(src, dest); result.push_back(edge); } } return result; } int main() { int V, E; cout << "请输入顶点数和边数:"; cin >> V >> E; vector<Edge> edges(E); cout << "请输入每条边的起点、终点和权重:" << endl; for (int i = 0; i < E; i++) { cin >> edges[i].src >> edges[i].dest >> edges[i].weight; } vector<Edge> mst = kruskalMST(edges, V); cout << "最小生成树的边:" << endl; for (const Edge& edge : mst) { cout << edge.src << " - " << edge.dest << " : " << edge.weight << endl; } return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值