25. 最小生成树

一、什么是最小生成树

  最小生成树(Minimum Cost Spanning Tree,简称 MST)问题是指假设给定一个带权的无向连通图,如何选取一棵树,使树上所有边上的权的总和为最小。最小生成树包含图中全部顶点和 N-1 条边。

二、Prim算法

  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 算法(克鲁斯卡尔算法)每次选择一条权值最小的边,使这条边的两头连通(原本已经连通的就不选),直到所有顶点都连通。

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);
        }
    }
}
posted @   星光映梦  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
点击右上角即可分享
微信分享提示