克鲁斯卡尔算法
基本思想
先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。时间复杂度为为O(e^2), 使用并查集优化后复杂度为 O(eloge),与网中的边数有关,适用于求边稀疏的网的最小生成树。
例图
C++代码
#include<iostream>
#include<climits>
using namespace std;
#define size 11
char change(int i) {
switch(i){
case 0: return 'A';
case 1: return 'B';
case 2: return 'C';
case 3: return 'D';
case 4: return 'E';
case 5: return 'F';
case 6: return 'G';
}
return 'O';
}
int a[7][7] = {
{0, 7, INT_MAX, 5, INT_MAX, INT_MAX, INT_MAX},
{7, 0, 8, 9, 7, INT_MAX, INT_MAX},
{INT_MAX, 8, 0, INT_MAX, 5, INT_MAX, INT_MAX},
{5, 9, INT_MAX, 0, 15, 6, INT_MAX},
{INT_MAX, 7, 5, 15, 0, 8, 9},
{INT_MAX, INT_MAX, INT_MAX, 6, 8, 0, 11},
{INT_MAX, INT_MAX, INT_MAX, INT_MAX, 9, 11, 0}
};
/*并查集*/
int visited[7] = {0, 1, 2, 3, 4, 5, 6};
typedef struct{
int start;
int end;
int len;
}Edge;
/*并查集*/
int Find(int x) {
if(visited[x] != x) visited[x] = Find(visited[x]);
return visited[x];
}
void Union(int x, int y){
int xParent = Find(x);
int yParent = Find(y);
if(xParent == yParent) return;
visited[yParent] = xParent;
}
int main() {
Edge edge[size];
int time = 0;
for(int i = 0; i < 7; i++)
for(int j = 0; j < i; j++) {
if(a[i][j] == INT_MAX) continue;
edge[time].start = i;
edge[time].end = j;
edge[time].len = a[i][j];
time++;
}
/*插入排序*/
for(int i = 1; i < size; i++) {
Edge key = edge[i];
while(i > 0 && edge[i-1].len > key.len) {
edge[i] = edge[i-1];
i--;
}
edge[i] = key;
}
int n = 6, count = 0, index = 0;
while(true) {
int x = edge[index].start;
int y = edge[index].end;
int len = edge[index].len;
int xParent = Find(x);
int yParent = Find(y);
index++;
if(xParent == yParent) {
continue;
}else{
Union(x, y);
count += len;
cout << "第" << 7 - n << "条边: " << change(x) << "->" << change(y) << endl;
n--;
}
if(n == 0) break;
}
cout << "最小生成树长度: " << count << endl;
}
/*
第1条边: D->A
第2条边: E->C
第3条边: F->D
第4条边: B->A
第5条边: E->B
第6条边: G->E
最小生成树长度: 39
*/