在Kruskal算法中,集合A是一个森林,其结点就是给定图的结点。每次加入到集合A中的安全边永远是权重最小的连接两个不同分量的边。在Prim算法中,集合A则是一棵树。每次加入到A中的安全边永远是连接A和A之外某个结点的边中权重最小的边。
Kruskal算法找到安全边的办法是,在所有连接森林中两棵不同的树的边里面,找到权重最小的边(u, v)。设C1和C2为边(u, v)所连接的两棵树。由于边(u, v)一定是连接C1和其他某棵树的一条轻量级边,边(u, v)是C1的一条安全边。Kruskal算法属于贪心算法,因为它每次都选择一条权重最小的边加入到森林。
Kruskal算法实现
在Kruskal实现过程中,使用一个不相交集合数据结构来维护几个互不相交的元素集合。每个集合代表当前森林中的一棵树。通过FindSet(u)来返回包含元素u集合的代表元素,通过Union来合并两棵树。
#include <stdio.h> #include <stdlib.h> #define BUFSIZE 100 #define VERTEXS 10 typedef struct { int u; int v; int weight; } edge; edge e[BUFSIZE] = {{'a', 'b', 4}, {'a', 'h', 8}, {'b', 'c', 8}, {'b', 'h', 11}, {'c', 'd', 7}, {'c', 'f', 4}, {'c', 'i', 2}, {'d', 'e', 9}, {'d', 'f', 14}, {'e', 'f', 10}, {'f', 'g', 2}, {'g', 'i', 6}, {'g', 'h', 1}, {'i', 'h', 7}}; // forest edge A[VERTEXS-1]; // 最小生成树的边集 int parent[VERTEXS]; // 记录前驱 int cost = 0; void MakeSet(int x) { parent[x] = 0; } int FindSet(int x) { while (parent[x] != 0) // 这里的判断parent[x]的初值有关 x = parent[x]; return x; } bool UnionSet(int x, int y, int w) { if (x != y) { // x, y不相同,将y合并到x中 parent[y] = x; return true; } return false; } void UnionSet(int x, int y) { parent[y] = x; } int Compare(const void *a, const void *b) { return ((edge *)a)->weight - ((edge *)b)->weight; } int main() { int n; // 边的个数 // printf("Please enter the number of edges:"); // scanf("%d", &n); // 输入边的信息 // for (int i = 0; i < n; i++) { // scanf(("%c %c %d"), &(e[i].u), &(e[i].v), &(e[i].weight)); // getchar(); // e[i].u -= 'a'; // e[i].v -= 'a'; // } n = 14; for (int i = 0; i < BUFSIZE; i++) { MakeSet(i); } // sort the edges of E into nondecreasing order by weight int cnt = 0; // A中元素个数(边数) qsort(e, n, sizeof(edge), Compare); for (int i = 0; i < n; i++) { int x = FindSet(e[i].u); // 判断u、v是否属于同一棵树 int y = FindSet(e[i].v); // 如果是就会形成环 if (x != y) { A[cnt].u = e[i].u; A[cnt].v = e[i].v; A[cnt++].weight = e[i].weight; UnionSet(e[i].u, e[i].v); // 将y合并到x中 printf("%c---%c:%d\n", e[i].u, e[i].v, e[i].weight); cost += e[i].weight; } } printf("Total weight:%d\n", cost); system("pause"); return 0; }