目录
一、Kruskal算法原理
Kruskal算法也叫避圈法,基于贪心策略,它通过不断选择图中权重最小的边来构建最小生成树。算法的主要思想是从所有边中按照权重从小到大进行选择,如果选择的边不会构成环(即不会形成闭合回路),就将该边加入最小成树中。为了避免环的形成,算法使用了并查集(Disjoint Set)数据结构来判断顶点是否连接。
二、Kruskal算法步骤
- 将所有边按照权重从小到大进行排序。
- 初始化并查集。
- 从排好序的边中选择权重最小的边。
- 如果该边连接的两个顶点不在同一个连通分量中(即不会形成环),则将该边加入最小生成树,并合并这两个顶点的连通分量。
- 重复步骤3和步骤4,直到最小生成树中包含了V-1条边,其中V为顶点数。
三、Kruskal算法实现
下面是代码中的图:
以下是使用C++实现Kruskal算法的示例代码:
1. 依据边权值从小到大对边排序
#define V 6 //顶点个数
int graph[V][V] = { //图的邻接矩阵表示
{0,6,0,0,5,1},
{6,0,3,0,0,5},
{0,3,0,6,0,6},
{0,0,6,0,2,4},
{5,0,0,2,0,4},
{1,5,6,4,4,0}
};
struct Edge { //存储边信息
int weight; //边的权值
int v1, v2; //边的两个顶点
//对<符号重载,方便依据weight进行排序
bool operator<(const Edge edge1)const {
return weight < edge1.weight;
}
};
//存储边的数组
vector<Edge> EdgeArray; //存储边信息的数组
//初始化EdgeArray数组
void initKruskal() {
Edge edge;
for (int i = 0; i < V-1; i++) {
for (int j = i+1; j < V; j++) {
if (graph[i][j]) {
edge.weight = graph[i][j];
edge.v1 = i;
edge.v2 = j;
EdgeArray.push_back(edge);
}
}
}
//依据weight进行排序
sort(EdgeArray.begin(), EdgeArray.end());
}
2. 并查集
//并查集的结构定义
int UFSet[V];
//并查集的初始化
void init() {
for (int i = 0; i < V; i++) {
UFSet[i] = -1; //每个元素都是一个集合
}
}
//查操作 返回v_index元素的树根的下标
int Find(int v_index) {
while (UFSet[v_index] >= 0) {
v_index = UFSet[v_index];
}
return v_index;
}
//合并操作
bool Union(int v1, int v2) {
int root1 = Find(v1); //v1元素的树根下标
int root2 = Find(v2); //v2元素的树根下标
//v1与v2已经连通
if (root1 == root2) {
return false;
}
else {
if (UFSet[root1] < UFSet[root2]) { //root1集合的元素更多
UFSet[root1] += UFSet[root2]; //修改root1的元素个数
UFSet[root2] = root1; //将root2并到root1
}
else { //root2集合的元素更多
UFSet[root2] += UFSet[root1]; //修改root2的元素个数
UFSet[root1] = root2; //将root1并到root2
}
return true;
}
}
3. Kruskal
//避圈法得到最小生成树
void KruskalMST() {
initKruskal(); //对边权值从小到大排序
init(); //初始化并查集
vector<Edge> order; //加入生成树的边的顺序
int weightSum = 0; //生成树的边权值和
int count = 0; //记录生成树中边的个数,有V-1条边时算法结束
for (int i = 0; i < EdgeArray.size()&&count!=V-1; i++) {
//能连到生成树中
if (Union(EdgeArray[i].v1, EdgeArray[i].v2)) {
weightSum += EdgeArray[i].weight;
order.push_back(EdgeArray[i]);
++count;
}
}
cout << "Kruskal生成的最小生成树的权值和为:" << weightSum << endl;
for (int i = 0; i < order.size(); i++) {
cout << "第" << i + 1 << "个加入生成树的边的两个顶点分别是是:"
<< order[i].v1<<" "<<order[i].v2
<< "\t其对应加入的边的权值是:" << order[i].weight << endl;
}
cout << endl;
}
四、完整代码
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define V 6 //顶点个数
int graph[V][V] = { //图的邻接矩阵表示
{0,6,0,0,5,1},
{6,0,3,0,0,5},
{0,3,0,6,0,6},
{0,0,6,0,2,4},
{5,0,0,2,0,4},
{1,5,6,4,4,0}
};
struct Edge { //存储边信息
int weight; //边的权值
int v1, v2; //边的两个顶点
//对<符号重载,方便依据weight进行排序
bool operator<(const Edge edge1)const {
return weight < edge1.weight;
}
};
//存储边的数组
vector<Edge> EdgeArray; //存储边信息的数组
//初始化EdgeArray数组
void initKruskal() {
Edge edge;
for (int i = 0; i < V-1; i++) {
for (int j = i+1; j < V; j++) {
if (graph[i][j]) {
edge.weight = graph[i][j];
edge.v1 = i;
edge.v2 = j;
EdgeArray.push_back(edge);
}
}
}
//依据weight进行排序
sort(EdgeArray.begin(), EdgeArray.end());
}
//并查集的结构定义
int UFSet[V];
//并查集的初始化
void init() {
for (int i = 0; i < V; i++) {
UFSet[i] = -1; //每个元素都是一个集合
}
}
//查操作 返回v_index元素的树根的下标
int Find(int v_index) {
while (UFSet[v_index] >= 0) {
v_index = UFSet[v_index];
}
return v_index;
}
//合并操作
bool Union(int v1, int v2) {
int root1 = Find(v1); //v1元素的树根下标
int root2 = Find(v2); //v2元素的树根下标
//v1与v2已经连通
if (root1 == root2) {
return false;
}
else {
if (UFSet[root1] < UFSet[root2]) { //root1集合的元素更多
UFSet[root1] += UFSet[root2]; //修改root1的元素个数
UFSet[root2] = root1; //将root2并到root1
}
else { //root2集合的元素更多
UFSet[root2] += UFSet[root1]; //修改root2的元素个数
UFSet[root1] = root2; //将root1并到root2
}
return true;
}
}
//避圈法得到最小生成树
void KruskalMST() {
initKruskal(); //对边权值从小到大排序
init(); //初始化并查集
vector<Edge> order; //加入生成树的边的顺序
int weightSum = 0; //生成树的边权值和
int count = 0; //记录生成树中边的个数,有V-1条边时算法结束
for (int i = 0; i < EdgeArray.size()&&count!=V-1; i++) {
//能连到生成树中
if (Union(EdgeArray[i].v1, EdgeArray[i].v2)) {
weightSum += EdgeArray[i].weight;
order.push_back(EdgeArray[i]);
++count;
}
}
cout << "Kruskal生成的最小生成树的权值和为:" << weightSum << endl;
for (int i = 0; i < order.size(); i++) {
cout << "第" << i + 1 << "个加入生成树的边的两个顶点分别是是:"
<< order[i].v1<<" "<<order[i].v2
<< "\t其对应加入的边的权值是:" << order[i].weight << endl;
}
cout << endl;
}
int main() {
KruskalMST(); //避圈法得到最小生成树
return 0;
}
五、运行结果
结果完全正确!!!!