25. 最小生成树
一、什么是最小生成树
最小生成树(Minimum Cost Spanning Tree,简称 MST)问题是指假设给定一个带权的无向连通图,如何选取一棵树,使树上所有边上的权的总和为最小。最小生成树包含图中全部顶点和 N-1 条边。
二、Prim算法
Prim 算法(普里姆算法)从某一个结点开始构建生成树,每次将代价最小但不构成回路的新顶点纳入生成树,直到所有顶点都纳入为止。
Prim 算法的实现与 Dijkstra 算法实现类型,只是这里的 dist 的值等于边的权重。
#include <limits.h>
/**
* @brief Prim算法
*
* @param G 图
*/
void Prim(Graph G)
{
int v = 0; // 开始顶点
int u = 0, w = 0;
int f = 1;
int dist[G->VertexNum]; // 存放顶点V到其它的最短距离
int path[G->VertexNum]; // 存放顶点V到其它顶点的路径
int collected[G->VertexNum]; // 存放顶点是否被收录的状态
MinHeap H = CreateMinHeap(G->VertexNum);
EdgeNode minItem = {0};
for (int i = 0; i < G->VertexNum; i++)
{
dist[i] = INT_MAX;
path[i] = -1;
collected[i] = 0;
}
dist[v] = 0;
minItem.V1 = v;
minItem.V2 = v;
minItem.Weight = dist[v];
Insert(H, minItem); // 将v入堆
while (H->Size != 0)
{
minItem = DeleteMin(H); // 未收录顶点中dist中的最小值
u = minItem.V1;
collected[u] = 1;
for (w = GetFirstNeighbor(G, u); w != -1; w = GetNextNeighbor(G, u, w)) // 遍历u的邻接点
{
if (collected[w] == 0 && (G->Matrix[u][w] < dist[w])) // 如果w没有被收录且v到w的距离大于v到u的距离加上u到w的距离
{
dist[w] = G->Matrix[u][w];
path[w] = u;
minItem.V1 = w;
minItem.Weight = dist[w];
Insert(H, minItem);
}
}
}
for (int i = 0; i < G->VertexNum; i++)
{
if (collected[i] != 1)
{
printf("顶点%c没有收录\n", G->Vertex[i]);
f = 0;
}
}
if (f)
{
printMST(G, path, dist, v);
}
}
/**
* @brief 打印最小生成树
*
* @param G 图
* @param path 路径集合
* @param dist 权重集合
* @param v 开始的顶点
*/
void printMST(Graph G, int path[], int dist[], int v)
{
for (int i = 0; i < G->VertexNum; i++)
{
if (i != v)
{
printf("边<%c, %c>,权值:%d\n", G->Vertex[path[i]], G->Vertex[i], dist[i]);
}
}
}
三、Kruskal算法
Kruskal 算法(克鲁斯卡尔算法)每次选择一条权值最小的边,使这条边的两头连通(原本已经连通的就不选),直到所有顶点都连通。
/**
* @brief Kruskal算法
*
* @param G 图
*/
void Kruskal(Graph G)
{
int i = 0;
EdgeNode edgeNode = {0};
EdgeNode MST[G->VertexNum];
MinHeap H = CreateMinHeap(G->EdgeNum);
DisjointSet * D = CreateDisjointSet();
for (int i = 0; i < G->VertexNum; i++)
{
for (int j = i + 1; j < G->VertexNum; j++)
{
if (G->Matrix[i][j] != 0)
{
edgeNode.V1 = i;
edgeNode.V2 = j;
edgeNode.Weight = G->Matrix[i][j];
Insert(H, edgeNode);
}
}
}
while (H->Size != 0)
{
edgeNode = DeleteMin(H);
int Root1 = Find(D, edgeNode.V1);
int Root2 = Find(D, edgeNode.V2);
if (Root1 != Root2)
{
MST[i++] = edgeNode;
Union(D, edgeNode.V1, edgeNode.V2);
}
}
if (i < G->VertexNum - 1)
{
printf("图不连通,无法生成最小生成树\n");
}
else
{
for (int i = 0; i < G->VertexNum - 1; i++)
{
printf("边<%c, %c>,权值:%d\n", G->Vertex[MST[i].V1], G->Vertex[MST[i].V2], MST[i].Weight);
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报