关于此算法的思想. 具体可以参照
最小生成树视频演示(bilibili上的)
此视频演示是我的头号参照, 大量文字叙述算法只会帮倒忙, 必要部分进行画图陈述
克鲁斯卡尔算法(有向图形式):
先将其封装为一个个的方法, 方便代码重用
/*
从上至下的方法作用:
*/
int getIndexOfRepresentative(const int index);
// 递归获取该编号节点的根节点索引
void merge(const int index1, const int index2);
// index1编号的集合与index2的集合合并, index2的根节点为合并后的树的根节点
bool isLoop(const int index1, const int index2);
// 判断要连接的两点是否会构成回路
方法示例:
getIndexOfRepresentative(1) = 4,
对getIndexOfRepresentative(3) = 4, getIndexOfRepresentative(7) = 4也是如此
首先, 我们先得有存储右侧edge的物品, 这个时候
先起一个定义 边的结构体!
struct edge {
int begin;
int end;
int len;
};
至于输入, 排序代码放在最后. 现在, 我们假定已经做好了排序, 完成了图中所示的结构, 那么接下来继续下一步
对
N
个结点,
M
条给定的无向边
我们先定义好
totalLength, 用于存放路径的总长度
addedEdges用于存放已经加入的边数
我们依据规则, 便能写出如下代码:
int N, M;
int totalLength = 0;
int addedEdges = 0;
cin >> N >> M;
edge *edges = new edge[M];
...
...
...
int i = 0;
while (i != M)
{
if (!(isLoop(edges[i].begin, edges[i].end)))
{
merge(edges[i].begin, edges[i].end);
addedEdges++;
totalLength += edges[i].len;
cout << edges[i].begin << " -> " << edges[i].end
<< " len:" << edges[i].len << endl;
if (addedEdges == (N - 1)) {
cout << totalLength << endl; return 0; }
}
i++;
}
从edges数组中
取一条最短的边, 判断加入后是否会构成回路, 否, 则将第一个集合与第二个连接起来, addedEdges自增一次, totalLength增加一次==
如图. 将索引为
0 4
的结点连接起来时
我们先判断是否构成回路, 没有构成回路, 进行下一步.
先将0 指向 4, 而 4 指向 6这个根节点
为了让所有结点最终都指向6
需要对索引0往回找, 直到找到根节点1
那么修改顺序如下:
1 —> 1 修改为 1 —> 5
5 —> 1 修改为 5 —> 0
0 —> 5 修改为 0 —> 4
(递归)
至此, 完成了连接.
(3是指向根节点1的, 所以1指向改变, 3随之改变)
#include<iostream>
#include<algorithm>
struct edge {
int begin;
int end;
int len;
};
static int nodes[5000];
/*
从上至下的方法作用:
递归获取该编号节点的根节点索引
index1编号的集合与index2的集合合并, index2的根节点为合并后的树的根节点
判断要连接的两点是否会构成回路
*/
int getIndexOfRepresentative(const int index);
void merge(const int index1, const int index2);
bool isLoop(const int index1, const int index2);
bool compare(edge e1, edge e2) {
return e1.len < e2.len;
}
int main(void) {
using namespace std