一、Prim算法
算法思想:从一个顶点开始构建最小生成树,每次选择一个键值最小的顶点加入最小生成树,并更新与该顶点相邻的顶点的键值。这样的话,就可以逐步构建出最小生成树。最终得到的最小生成树是连接所有顶点并具有最小总权重的树。
求解步骤:
1.初始化一个空的最小生成树集合和一个顶点集合。
2.选择任意一个顶点作为起始点,将其加入最小生成树集合。
3.重复以下步骤,直到最小生成树集合包含了所有顶点:
①从最小生成树集合连接到顶点集合的边中选择权重最小的边。
②将该边连接的顶点加入最小生成树集合。
代码示例:
c
#include <stdio.h>
#include <stdbool.h>
#define INF 9999
#define V 5
int minKey(int key[], bool mstSet[])
{
int min = INF, minIndex;
// 寻找未加入最小生成树的顶点中具有最小键值的顶点
for (int v = 0; v < V; v++)
{
if (mstSet[v] == false && key[v] < min)
{
min = key[v];
minIndex = v;
}
}
return minIndex;
}
void printMST(int parent[], int graph[V][V])
{
// 打印最小生成树的边和权重
for (int i = 1; i < V; i++)
printf("%d - %d %d \n", parent[i], i, graph[i][parent[i]]);
}
void primMST(int graph[V][V])
{
int parent[V]; // 存储最小生成树的父节点
int key[V]; // 存储每个顶点的键值
bool mstSet[V]; // 记录顶点是否已加入最小生成树
// 初始化键值为无穷大,所有顶点都未加入最小生成树
for (int i = 0; i < V; i++)
{
key[i] = INF;
mstSet[i] = false;
}
key[0] = 0; // 从第一个顶点开始构建最小生成树
parent[0] = -1; // 第一个顶点没有父节点
// 构建 V-1 条边,每次选取一个顶点加入最小生成树
for (int count = 0; count < V - 1; count++)
{
int u = minKey(key, mstSet); // 选择键值最小的顶点加入最小生成树
mstSet[u] = true; // 将顶点加入最小生成树
// 更新与 u 相邻的顶点的键值
for (int v = 0; v < V; v++)
{
// 仅考虑未加入最小生成树的顶点,并更新键值
if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v])
{
parent[v] = u;
key[v] = graph[u][v];
}
}
}
printMST(parent, graph); // 打印最小生成树
}
int main()
{
int graph[V][V] = {{0, 2, 0, 6, 0},
{2, 0, 3, 8, 5},
{0, 3, 0, 0, 7},
{6, 8, 0, 0, 9},
{0, 5, 7, 9, 0}};
primMST(graph); // 调用 Prim 算法求解最小生成树
return 0;
}
minKey 函数用于寻找未加入最小生成树的顶点中具有最小键值的顶点。
printMST 函数用于打印最小生成树的边和权重。
primMST 函数初始化键值和最小生成树集合,构建最小生成树的主循环,以及更新键值的操作。
二、Kruskal算法:
算法思想:将图的所有边按照权重进行排序,然后依次选择权重最小的边,如果选择该边不会形成环路,则将其加入最小生成树,直到最小生成树的边数达到 V-1。最终得到的最小生成树是连接所有顶点并具有最小总权重的树。
求解步骤:
1.初始化一个空的最小生成树集合和一个边集合。
2.将图中的所有边按照权重进行排序。
3.重复以下步骤,直到最小生成树集合包含了所有顶点-1条边:
①选择权重最小的边,如果该边的两个顶点不在同一个连通分量中,则将该边加入最小生成树集合,并合并两个连通分量。
代码示例:
c
#include <stdio.h>
#include <stdbool.h>
#define V 5
// 边的结构体
struct Edge {
int src, dest, weight;
};
// 图的结构体
struct Graph {
int V, E;
struct Edge* edge;
};
struct Graph* createGraph(int V, int E) {
struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));
graph->V = V;
graph->E = E;
graph->edge = (struct Edge*)malloc(graph->E * sizeof(struct Edge));
return graph;
}
// 比较函数用于排序边
int compare(const void* a, const void* b) {
struct Edge* a1 = (struct Edge*)a;
struct Edge* b1 = (struct Edge*)b;
return a1->weight - b1->weight;
}
int find(int parent[], int i) {
if (parent[i] == -1)
return i;
return find(parent, parent[i]);
}
void Union(int parent[], int x, int y) {
int xset = find(parent, x);
int yset = find(parent, y);
parent[xset] = yset;
}
void printMST(struct Edge result[], int e) {
for (int i = 0; i < e; i++)
printf("%d - %d %d \n", result[i].src, result[i].dest, result[i].weight);
}
void kruskalMST(struct Graph* graph) {
int V = graph->V;
struct Edge result[V];
int e = 0;
int i = 0;
// 对边进行排序
qsort(graph->edge, graph->E, sizeof(graph->edge[0]), compare);
int* parent = (int*)malloc(V * sizeof(int));
// 初始化父节点数组
memset(parent, -1, sizeof(int) * V);
while (e < V - 1 && i < graph->E) {
struct Edge next_edge = graph->edge[i++];
int x = find(parent, next_edge.src);
int y = find(parent, next_edge.dest);
// 如果选择这条边不会产生环路,则选择它
if (x != y) {
result[e++] = next_edge;
Union(parent, x, y);
}
}
printMST(result, e);
}
int main() {
int V = 5; // 顶点数
int E = 7; // 边数
struct Graph* graph = createGraph(V, E);
// 添加边
graph->edge[0].src = 0;
graph->edge[0].dest = 1;
graph->edge[0].weight = 2;
graph->edge[1].src = 0;
graph->edge[1].dest = 3;
graph->edge[1].weight = 6;
graph->edge[2].src = 1;
graph->edge[2].dest = 2;
graph->edge[2].weight = 3;
graph->edge[3].src = 1;
graph->edge[3].dest = 4;
graph->edge[3].weight = 5;
graph->edge[4].src = 2;
graph->edge[4].dest = 4;
graph->edge[4].weight = 7;
graph->edge[5].src = 3;
graph->edge[5].dest = 4;
graph->edge[5].weight = 9;
graph->edge[6].src = 2;
graph->edge[6].dest = 3;
graph->edge[6].weight = 8;
kruskalMST(graph); // 调用 Kruskal 算法求解最小生成树
return 0;
}