【算法】Kruskal算法
前言
这篇博客来记录Kruskal算法,我比较奇怪的是课本居然没给实现,那我就按自己的理解实现一遍好了。
简介
Kruskal是一种求最小生成树的算法,相比Prim算法的加点策略,Kruskal算法采用加边策略.。
算法步骤描述
假设\(N=(V,{E})\)为一连通图,将\(N\)中的边按从小到大顺序排列
- 将\(n\)个顶点看作\(n\)个集合
- 按权值由小到大选择边,所选边应满足两个顶点不在同一顶点集合内,将该边放到生成树边的集合中,同时将该边两个顶点所在顶点集合合并
- 重复2直至所有顶点均在同一顶点集合内。
算法流程图示

算法实现
为了实现判断不同顶点是否在同一集合内,我们使用并查集作为辅助数据结构来使用,定义如下:
struct VertexSet
{
int vertex; // 表示当前结点
VertexSet *parent = nullptr;
VertexSet() = default;
VertexSet(int i) : vertex(i) {} // 构造函数
void changeGroup(VertexSet *set)
{ // 将this结点加入到集合set里
if (this->parent == nullptr)
{
this->parent = set; // this无父节点则直接将this接入set
}
else
{
auto temp = this; // 有父节点则寻找顶层父节点,将该父节点并入set
while (temp->parent != nullptr)
{
temp = temp->parent;
}
temp->parent = set;
}
}
bool inGroup(VertexSet *set)
{ // 看结点this与set是否在同一集合
VertexSet *temp = this;
// 寻找顶层父节点
while (temp != nullptr && temp->parent != nullptr)
{
temp = temp->parent;
}
VertexSet *tempSet = set;
while (tempSet != nullptr && tempSet->parent != nullptr)
{
tempSet = tempSet->parent;
}
//////////////////////////////////////////////////
// 比较
if (temp == tempSet)
{
return true;
}
return false;
}
};
而Kruskal算法主体实现如下
void Tree::kruskal()
{
std::sort(arcs.begin(), arcs.end(), [](Arc A, Arc B)
{ return A.weight < B.weight; }); // 使用std::sort为边集合排序
VertexSet vertexSet[vexnum + 1]; // 创建适当大小的并查集
for (size_t i = 1; i <= vexnum; i++)
{
vertexSet[i] = VertexSet(i); // 初始化并查集
}
int now = 0, size = vexnum; // size用于记录停止条件
while (size != 1)
{
int begin = arcs[now].tail;
int end = arcs[now].head;
if (vertexSet[begin].inGroup(&vertexSet[end]))
{
now++;
continue;
}
std::cout << "<" << begin << "," << end << ">" << std::endl;
now++;
vertexSet[begin].changeGroup(&vertexSet[end]);
size--;
}
}
使用了std::sort来进行快速排序,可以说是偷了点懒吧┑( ̄Д  ̄)┍
完整代码
#include <iostream>
#include <vector>
#include <algorithm>
struct Arc
{
int tail;
int head;
int weight;
Arc(int t, int h, int w) : tail(t), head(h), weight(w) {}
Arc() = default;
};
struct Tree
{
std::vector<Arc> arcs;
int vexnum;
int arcnum;
void init();
void kruskal();
};
struct VertexSet
{
int vertex;
VertexSet *parent = nullptr;
VertexSet() = default;
VertexSet(int i) : vertex(i) {}
void changeGroup(VertexSet *set)
{
if (this->parent == nullptr)
{
this->parent = set;
}
else
{
auto temp = this;
while (temp->parent != nullptr)
{
temp = temp->parent;
}
temp->parent = set;
}
}
bool inGroup(VertexSet *set)
{
VertexSet *temp = this;
while (temp != nullptr && temp->parent != nullptr)
{
temp = temp->parent;
}
VertexSet *tempSet = set;
while (tempSet != nullptr && tempSet->parent != nullptr)
{
tempSet = tempSet->parent;
}
if (temp == tempSet)
{
return true;
}
return false;
}
};
void Tree::init()
{
std::cout << "请输入顶点个数与边个数:";
std::cin >> vexnum >> arcnum;
for (size_t i = 1; i <= arcnum; i++)
{
int begin, end, weight;
std::cout << "请输入弧尾弧头与两点间距离:";
std::cin >> begin >> end >> weight;
arcs.emplace_back(std::move(Arc(begin, end, weight)));
}
}
void Tree::kruskal()
{
std::sort(arcs.begin(), arcs.end(), [](Arc A, Arc B)
{ return A.weight < B.weight; });
VertexSet vertexSet[vexnum + 1];
for (size_t i = 1; i <= vexnum; i++)
{
vertexSet[i] = VertexSet(i);
}
int now = 0, size = vexnum;
while (size != 1)
{
int begin = arcs[now].tail;
int end = arcs[now].head;
int weight = arcs[now].weight;
if (vertexSet[begin].inGroup(&vertexSet[end]))
{
now++;
continue;
}
std::cout << "<" << begin << "," << end << ">" << std::endl;
now++;
vertexSet[begin].changeGroup(&vertexSet[end]);
size--;
}
}
int main()
{
Tree aTree;
aTree.init();
aTree.kruskal();
}
图使用邻接矩阵存储。

浙公网安备 33010602011771号