一、最小生成树
1.1 生成树的定义
一个连通图的生成树是⼀个极小的连通子图,它包含图中全部的n个顶点,但只有构成⼀棵树的n-1条边

连通图和它相对应的⽣成树,可以⽤于解决实际生活中的问题:假设A、B、C 和 D 为 4 座城市,
为了⽅便⽣产⽣活,要为这 4 座城市建⽴通信。对于 4 个城市来讲,本着节约经费的原则,只需要建⽴3 个通信线路即可,就如图(b)中的任意⼀种⽅式。
在具体选择采用图(b)中哪⼀种⽅式时,需要综合考虑城市之间间隔的距离,建设通信线路的难度等各种因素,将这些因素综合起来⽤⼀个数值表示,当作这条线路的权值。

综合所看,选择权值总和为7的两种⽅案最节约经费
1.2生成树的属性
⼀个连通图可以有多个⽣成树;
⼀个连通图的所有生成树都包含相同的顶点个数和边数;
生成树当中不存在环;
移除生成树中的任意⼀条边都会导致图的不连通, 生成树的边最少特性;
在⽣成树中添加⼀条边会构成环。 对于包含n个顶点的连通图,⽣成树包含n个顶点和n-1条边;
对于包含n个顶点的无向完全图最多包含n^(n-2)颗生成树。
1.3最小生成树
所谓⼀个 带权图 的最小生成树,就是原图中边的权值最小的生成树。
最小生成树的算法思想就是从顶点集或边集的角度来进行贪婪化。
二、Kruskal算法
代码:
#ifndef KRUSKAL_H
#define KRUSKAL_H
#include "../base.h"
#include "../01.MatrixGraph/matrixGraph.h"
/* Kruskal算法
* 从边的角度求图的最小生成树,更适合求边稀疏的图
* 算法思路:
* a. 将所有的边按照权值的大小进行升序排序,然后从小到大一一判断,判断条件:
* 1. 如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;
* 2. 放置,舍去
* b. 循环a,直到具有n个顶点的连通图筛选出n-1条边为止
* 引入一个数据结构,边集数组,保存边和节点的关系
* 简化版:从边集数组里,找最小的边,判断2个顶点会不会构成一个环,如果不够,加入结果
* */
// 使用邻接矩阵表示了无向图,从邻接矩阵中初始化边集数组
int initEdgeSet(const MGraph *graph, EdgeSet *edges);
// 排序边集数组
void sortEdgeSet(EdgeSet *edges, int num);
// Kruskal算法
int KruskalMGraph(const MGraph *graph, const EdgeSet *edges, int num, EdgeSet *result);
#endif
#include <string.h>
#include <stdlib.h>
#include "Kruskal.h"
/* 邻接矩阵无向图,只需要上三角数据即可 */
//把邻接矩阵变成边集数组,边集数组就是把边放进一个数组
int initEdgeSet(const MGraph *graph, EdgeSet *edges) {
int k = 0;
for (int i = 0; i < graph->nodeNum; ++i) { // 遍历每个节点
for (int j = i + 1; j < graph->nodeNum; ++j) { // 遍历每个节点,所有的边,i+1,遍历上三角(每一行从对角线开始遍历)
//ij的边存在,则放入边集数组中
if (graph->edges[i][j] > 0) {
edges[k].begin = i;
edges[k].end = j;
edges[k].weight = graph->edges[i][j];
k++;
}
}
}
return k;
}
/* 按照边的权值从小到大进行排序
* 冒泡方法,第i个元素和后面元素比较,如果后面的权值小,交换
* 通过一次遍历,当前的i就是最小值
* */
void sortEdgeSet(EdgeSet *edges, int num) {
EdgeSet tmp;
for (int i = 0; i < num; ++i) {
for (int j = i + 1; j < num; ++j) {
//j是i的下一个边,如果j的权值小于i,则i与j交换位置。
if (edges[j].weight < edges[i].weight) {
//两个空间的交换
memcpy(&tmp, &edges[i], sizeof(EdgeSet));
memcpy(&edges[i], &edges[j], sizeof(EdgeSet));
memcpy(&edges[j], &tmp, sizeof(EdgeSet));
}
}
}
}
// 从并查集中,找a节点的根节点
static int getRoot(const int *uSet, int a) {
while (a != uSet[a]) {
a = uSet[a];
}
return a;
}
//Kruskal算法
int KruskalMGraph(const MGraph *graph, const EdgeSet *edges, int num, EdgeSet *result) {
int *uSet;
int a;
int b;
int count = 0;
int sum = 0;
// 1. 初始化并查集,每一个节点的编号就是自己
uSet = (int *) malloc(sizeof(int ) *graph->nodeNum);
for (int i = 0; i < graph->nodeNum; ++i) {
uSet[i] = i;
}
// 2. 从已经排序好的边集中,找到最小的边(顺序找),当这个边加入后,不构成闭环,就可以加入结果
for (int i = 0; (count < (graph->nodeNum - 1)) && i < num; ++i) {
a = getRoot(uSet, edges[i].begin);
b = getRoot(uSet, edges[i].end);
if (a != b) { // 不构成闭环
uSet[a] = b; //b加入a的并查集,且b为a的老爸
result[count].begin = edges[i].begin;
result[count].end = edges[i].end;
result[count].weight = edges[i].weight;
count++;
sum += edges[i].weight;
}
}
free(uSet);//释放并查集
return sum;
}
#include <stdlib.h>
#include <stdio.h>
#include "Kruskal.h"
static void setupMGraph(MGraph *graph) {
char *names[] = {"A", "B", "C", "D",
"E", "F", "G"};
//初始化:图,顶点个数,names,无向图0,边数为0
initMGraph(graph, sizeof(names)/ sizeof(names[0]), names, 0, 0);
//添加边
addMGraphEdge(graph, 0, 1, 12);//边AB,权值12
addMGraphEdge(graph, 0, 5, 16);
addMGraphEdge(graph, 0, 6, 14);
addMGraphEdge(graph, 1, 2, 10);
addMGraphEdge(graph, 1, 5, 7);
addMGraphEdge(graph, 2, 3, 3);
addMGraphEdge(graph, 2, 4, 5);
addMGraphEdge(graph, 2, 5, 6);
addMGraphEdge(graph, 3, 4, 4);
addMGraphEdge(graph, 4, 5, 2);
addMGraphEdge(graph, 4, 6, 8);
addMGraphEdge(graph, 5, 6, 9);
}
int main() {
MGraph graph;//邻接矩阵
EdgeSet *edges;
setupMGraph(&graph);
//申请边
edges = (EdgeSet *) malloc(sizeof(EdgeSet) * graph.edgeNum);
int num = initEdgeSet(&graph, edges);//初始化边集数组
printf("edgeSet num: %d\n", num);
sortEdgeSet(edges, num);
//结果(n-1条边)
EdgeSet *result = (EdgeSet *) malloc(sizeof(EdgeSet) * (graph.nodeNum - 1));
//总权值
int sumW = KruskalMGraph(&graph, edges, num, result);
printf("Kruskal sum of weight: %d\n", sumW);
//打印输出边的情况
//点---权值---点(A---12---B)
for (int i = 0; i < graph.nodeNum - 1; ++i) {
printf("edge %d: [%s] --- <%d> --- [%s]\n", i + 1,
graph.vex[result[i].begin].show, result[i].weight, graph.vex[result[i].end].show);
}
free(edges);
free(result);
return 0;
}
三、Prim算法