图论——最小生成树Kruskal算法

必备前置知识算法学习------并查集-CSDN博客

一、基本定义

1. 生成树(Spanning Tree)

对于一个连通无向图,它的生成树是指:

  • 包含原图的所有顶点(n个)

  • 只有n-1条边

  • 仍然保持连通性

  • 没有环(acyclic)

2. 最小生成树(Minimum Spanning Tree, MST)

当图中的边有权重(成本、长度、代价)时,在所有的生成树中,边权重之和最小的那个生成树就是最小生成树。

二、核心特性

1. 无环性(Acyclic)

最小生成树中不能有环,因为如果有环,就可以去掉环上的一条边而仍然保持连通,从而减小总权重。

2. 连通性(Connected)

必须连接所有顶点,不能有孤立的顶点或连通分量。

3. 最少边数

恰好有n-1条边(n为顶点数):

  • 少于n-1条边:无法连通所有顶点

  • 多于n-1条边:必然形成环

三、Kruskal算法

Kruskal算法是一种贪心算法 ,通过并查集 高效判断连通性,适合解决稀疏图的最小生成树问题。其核心是"按边选择,避免成环",时间复杂度主要由排序决定,为O(m log m)。

四、贪心策略

每次选择权值最小 的边,如果加入这条边不会形成环,则将其加入生成树中。

  1. 将图中所有边按权值从小到大排序

  2. 初始化并查集father数组,每个节点指向自己

  3. 依次检查排序后的每条边(x,y,z):

a. 如果加入这条边不会形成环,即x和y不在同一个集合,即find(x) ≠ find(y)

b. 计数器cnt+1,总权重ans+z,合并x和y所在的集合(Union)

c. 如果cnt=n-1,算法结束;否则说明图不连通

五、代码实现

1.头文件与命名空间

cpp 复制代码
#include<iostream>
#include<algorithm>//sort
#include<vector>
#include<stack>
using namespace std;

2.全局变量定义

cpp 复制代码
const int N = 2e5 + 10;
// 并查集数组,存储每个节点的父节点
int father[N];
int n, m; // n:节点数,m:边数

3.数据结构定义

cpp 复制代码
struct edge // 边结构体
{
	int x;
	int y;
	int z;
};
// 存储所有边的动态数组
vector <struct edge> edges;

4.比较函数

cpp 复制代码
bool cmp(struct edge a, struct edge b)
{
    // 按边权重从小到大排序(Kruskal算法核心)
	return a.z < b.z;   
}

5.并查集操作函数

cpp 复制代码
//1.初始化并查集
void build()
{
    // 每个节点的父节点初始化为自身
	for (int i = 1;i <= n;i++)
		father[i] = i;
}

//2.查找节点i的根节点
int find(int i)
{
	stack <int> st;// 使用栈记录查找路径
	while (father[i] != i)// 当i不是根节点时
	{
		st.push(i);// 将当前节点压入栈
		i = father[i];// 向上查找父节点
	}
	while (!st.empty())// 路径压缩优化
	{
    // 将路径上所有节点的父节点设为根节点
		father[st.top()] = i;
		st.pop();// 弹出栈顶
	}
	return i; // 返回根节点
}

//3.合并两个集合
bool Union(int x, int y)
{
	//保存第一次find()的结果
	//避免重复调用可能带来的问题
	int fx = find(x);    // 查找x的根节点
	int fy = find(y);    // 查找y的根节点
	if (fx != fy)        // 如果根节点不同
	{
		father[fx] = fy; // 合并两个集合
		return true; // 返回true表示合并成功
	}
	return false; // 返回false表示已在同一集合
}

6.主函数

cpp 复制代码
int main()
{
	cin >> n >> m; // 输入节点数和边数
	build();// 初始化并查集(必须在知道n之后)
	for (int i = 1;i <= m;i++)
	{
		int x = 0, y = 0, z = 0;
		cin >> x >> y >> z;// 输入一条边的信息
		edges.push_back({ x,y,z });// 将边加入数组
	}
    // 按边权重升序排序
    sort(edges.begin(), edges.end(), cmp);
	int cnt = 0;// 记录已选边数(最小生成树需要n-1条边)
	int ans = 0;// 记录最小生成树总权重
	for (auto e : edges) // 遍历所有排序后的边
	{
        // 如果边的两端点不在同一连通块
		if (Union(e.x, e.y))
		{
			cnt++;     // 选择这条边
			ans += e.z;// 累加权重
		}
        // 已选够n-1条边(最小生成树完成)
		if (cnt == n - 1)
			break;// 提前结束循环
	}
    if (cnt == n - 1) // 如果成功构建最小生成树
		cout << ans << endl;// 输出总权重
	else // 如果图不连通
		cout << "impossible" << endl;// 输出不可能
	return 0;
}

7.所有代码

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
const int N = 2e5 + 10;
int father[N];
int n, m;
struct edge
{
	int x;
	int y;
	int z;
};
vector <struct edge> edges;
bool cmp(struct edge a, struct edge b)
{
	return a.z < b.z;
}
void build()
{
	for (int i = 1;i <= n;i++)
		father[i] = i;
}
int find(int i)
{
	stack <int> st;
	while (father[i] != i)
	{
		st.push(i);
		i = father[i];
	}
	while (!st.empty())
	{
		father[st.top()] = i;
		st.pop();
	}
	return i;
}
bool Union(int x, int y)
{
	//保存第一次find()的结果
	//避免重复调用可能带来的问题
	int fx = find(x);
	int fy = find(y);
	if (fx != fy)
	{
		father[fx] = fy;
		return true;
	}
	return false;
}
int main()
{
	cin >> n >> m;
	//要在输入n之后调用build
	build();
	for (int i = 1;i <= m;i++)
	{
		int x = 0, y = 0, z = 0;
		cin >> x >> y >> z;
		edges.push_back({ x,y,z });
	}
	sort(edges.begin(), edges.end(), cmp);
	int cnt = 0;
	int ans = 0;
	for (auto e : edges)
	{
		if (Union(e.x, e.y))
		{
			cnt++;
			ans += e.z;
		}
		if (cnt == n - 1)
			break;
	}
	if (cnt == n - 1)
		cout << ans << endl;
	else
		cout << "impossible" << endl;
	return 0;
}
相关推荐
宇木灵1 小时前
C语言基础-十一、递归与分治(完结)
c语言·开发语言·学习·算法
We་ct2 小时前
LeetCode 173. 二叉搜索树迭代器:BSTIterator类 实现与解析
前端·算法·leetcode·typescript
weixin_395448912 小时前
main.c_0222cursor
c语言·前端·算法
Zik----2 小时前
Leetcode27 —— 移除元素(双指针)
数据结构·算法
陆嵩3 小时前
GMRES 方法的数学推导及其算法表示
算法·概率论·arnoldi·gmres·minres·givens·hessenberg
plus4s3 小时前
2月22日(94-96题)
算法
tankeven3 小时前
HJ98 喜欢切数组的红
c++·算法
adore.9683 小时前
2.22 oj基础92 93 94+U12
数据结构·c++·算法
颜酱4 小时前
前缀和技巧全解析:从基础到进阶
javascript·后端·算法