算法题(234):最小生成树(kruskal算法)
审题:
本题是最小生成树的模板题思路:
方法一:kruskal算法kruskal算法关注的是边
1.所有边按权值从小到大排序
2.每次选出权值最小且两端点不连通的边加入最小生成树,直到所有顶点都连通
解题:
(1)数据初始化#include<iostream> #include<algorithm> using namespace std; const int N = 5010, M = 2e5 + 10, INF = 0x3f3f3f3f; int n, m; int f[N]; struct edge {int x, y, z; }e[M];
f[N]是并查集数组(树状结构),他的第i个位置存储的是i号节点的父节点,当f[i]==i时表示i是一个集合的标识元素,是树结构的根节点
edge结构体用于存储边信息
(2)main函数
int main() {cin >> n >> m;for (int i = 1; i <= m; i++){cin >> e[i].x >> e[i].y >> e[i].z;}//初始化并查集for (int i = 1; i <= n; i++){f[i] = i;}int ret = 0;ret = kruskal();if (ret == INF)cout << "orz" << endl;elsecout << ret << endl;return 0; }
1.初始化并查集:
直接让f[i]==i即可,表示一开始所有节点都是一个集合,处于同一集合表示连通,否则表示非连通
(3)kruskal函数
//比较函数 bool cmp(edge& a, edge& b) {return a.z < b.z; } //并查集查询函数 int find(int num) {return f[num] == num ? num : f[num] = find(f[num]); } int kruskal() {int ret = 0;int cnt = 0;//记录生成树边条数sort(e + 1, e + 1 + m, cmp);for (int i = 1; i <= m; i++){int x = e[i].x, y = e[i].y, z = e[i].z;int fx = find(x), fy = find(y);if (fx == fy)//连通节点{continue;}else//非连通节点{ret += z;f[fx] = fy;cnt++;}}if (cnt != n-1)//非连通图{return INF;}return ret; }
1.边权值排序
使用sort函数并内置我们自己实现的cmp比较函数
其中cmp函数是根据边的z变量(权值)进行比较的
2.判断端点是否连通
使用基于并查集的find函数,find函数会返回当前端点的根节点,若两个端点根节点相同说明是连通状态,直接跳过当前边。否则就更新ret(总权值),并将集合合并,cnt++(用于和n-1比较,判断最终是否构成了最小生成树)
对于解决最小生成树的两种算法:prim和kruskal算法
时间复杂度区别
1.prim:n^2
2.kruskal: m*logm
prim和节点有关系,kruskal和边有关系,虽然一般情况下两种算法都可以使用
但是当图的边特别多时,使用prim算法会更好
从实现难度来说,使用kruskal算法更好写,不容易出错
P3366 【模板】最小生成树 - 洛谷