打算出两个系列,对最小生成树的原理和代码实现进行讲解。由于最小生成树的代码在很多教科书和网站都有讲解,所以这里的重点是代码的实现。本文的代码语言为C++,《大话数据结构》中用的是C语言,各位可以参考下。本文的主要实现是基于B站的up主@WAY_zhong的动画,链接为:最小生成树(Kruskal(克鲁斯卡尔)和Prim(普里姆))算法动画演示述
十分推荐大家去看,该视频生动地描述了最小生成树的原理,本文的代码就是基于该视频的。
代码的基本思想
生成树的基本概念
最小生成树代码,包括普利姆算法和克鲁斯卡尔算法(都基于贪婪算法)
- 最小生成树是树,所以不能形成环
- 需要连接所有顶点
- N个顶点会有N-1条边
- 在所有生成树中,最小生成树要求权值和最小
克鲁斯卡尔算法
克鲁斯卡尔算法(基本思想:直接选择那些权值最小的边,判断这些边是否可以构成树/如果可以,添加进结果集,如果不可以就舍弃
步骤:
- 将所有边放入一个列表中,并按权值从小到大开始排序
- 从列表中从头开始取出一条边,将图中离散的点进行连接,如果没有构成环就添加,构成环就舍弃
代码实现
先给出整体代码,再对代码各部分模块进行分析
#include<vector>
#include<iostream>
#include<algorithm>
#define max INT_MAX
using namespace std;
//定义边集数组结构(该数组就是第1步中所说的列表)
struct EdgWeigt
{
int begin;
int end;
int weight;
};
//并查集的find函数,用于判断是否存在环
class Djset
{
vector<int> parent;
vector<int> rank;
int N;
public:
Djset(){
}
Djset(vector<int> &parent, vector<int> &rank, int N):parent(parent), rank(rank), N(N)
{
}
int find(int x)
{
int x_root = x;
while (parent[x_root] != -1)
{
x_root = parent[x_root];
}
return x_root;
}
void merge(int x, int y)
{
int x_root = find(x);
int y_root = find(y);
if (x_root == y_root) return;
else {
if (rank[x_root] > rank[y_root])
{
parent[y_root] = x_root;
}
else if (rank[y_root] > rank[x_root])
{
parent[x_root] = y_root;
}
else if (rank[y_root] == rank[x_root])
{
parent[x_root] = y_root;
rank[y_root] ++;
}
}
}
};
void Kruskal(vector<vector<int>> &graph)
{
//在这里,输入的图为无向图,所以权值邻接矩阵是对称的
vector<EdgWeigt> edgeArr;//边集数组
//初始化边集数组
for (int i = 0; i < graph.size(); ++i)
{
for (int j = i+1; j < graph[i].size(); ++j)
{
EdgWeigt edge;
edge.begin = i;
edge.end = j;
edge.weight = graph[i][j]