【C语言—最小生成树】普里姆(Prime)算法和克鲁斯卡尔(Kruskal)算法

博客介绍了图及其邻接矩阵,重点阐述寻找图的最小生成树的普里姆(Prime)算法和克鲁斯卡尔(Kruskal)算法。详细说明了两种算法的实现步骤并给出代码实现思路,最后总结指出克鲁斯卡尔算法适合稀疏图,普里姆算法适合稠密图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图及其邻接矩阵

在这里插入图片描述
寻找该图的最小生成树(包括原图的n个顶点,n-1条边)

普里姆(Prime)算法

实现步骤

1、从顶点入手,假设以V0顶点作为该图最小生成树的根节点。
2、V0顶点有两条边其权值分别为10和11,我们选择最小权值的边,即选中V1顶点。
3、接下来在V0和V1的边中寻找最小权值的边,为11,即选中V5顶点。
4、同理,依次选中V8、V2、V6顶点。
5、此时再寻找最小权值的边时为(V5,V6)其权值为17。但此时V5已经通过V0、V1与V6连通了,因此选择权值为19的(V6, V7),即选中V7顶点。
6、同理,之后依次选择V4、V3顶点。

代码实现

/*
 * @Author: Xyh4ng
 * @Date: 2022-10-29 18:32:06
 * @LastEditors: Xyh4ng
 * @LastEditTime: 2022-10-29 21:30:38
 * @Description: 普里姆算法
 * Copyright (c) 2022 by Xyh4ng 503177404@qq.com, All Rights Reserved.
 */
#include <stdio.h>
#include <stdlib.h>

#define INFINITY 1000

typedef struct Graph
{
    int (*adjMatrix)[9];
    int data[9];
    int numNodes, numEdges;
} Graph;

void Prime(Graph *G)
{
    int k = 0;
    int lowCost[9]; // 用来记录已选中顶点与各个未选中顶点的最小权值(为0说明该索引顶点已经被选中)
    int adjVex[9];  // 用来记录该索引顶点与哪个顶点相通
    lowCost[0] = 0; // 先选中v0顶点
    adjVex[0] = 0;
    printf("%d", G->data[0]);
    for (int i = 1; i < G->numNodes; i++)
    {
        lowCost[i] = G->adjMatrix[0][i];
        adjVex[i] = 0;
    }
    for (int i = 1; i < G->numNodes; i++)
    {
        // 选择出可选边中的最小权值,并将新增顶点的索引赋值给k
        int min = INFINITY;
        for (int j = 1; j < G->numNodes; j++)
        {
            if (lowCost[j] != 0 && lowCost[j] < min)
            {
                min = lowCost[j];
                k = j;
            }
        }
        printf("%d", G->data[k]);
        lowCost[k] = 0;
        // 遍历新增顶点的边,并更新已选中顶点与未选中顶点相通的最小权值的边
        for (int j = 1; j < G->numNodes; j++)
        {
            if (lowCost[j] != 0 && G->adjMatrix[k][j] < lowCost[j])
            {
                lowCost[j] = G->adjMatrix[k][j];
                adjVex[j] = k;
            }
        }
    }
}

int main()
{
    Graph *G = (Graph *)malloc(sizeof(Graph));
    G->numNodes = 9;
    G->numEdges = 15;
    for (int i = 0; i < G->numNodes; i++)
    {
        G->data[i] = i;
    }
    int edgeMatrix[9][9] = {
        {0, 10, INFINITY, INFINITY, INFINITY, 11, INFINITY, INFINITY, INFINITY},
        {10, 0, 18, INFINITY, INFINITY, INFINITY, 16, INFINITY, 12},
        {INFINITY, 18, 0, 22, INFINITY, INFINITY, INFINITY, INFINITY, 8},
        {INFINITY, INFINITY, 22, 0, 20, INFINITY, 24, 16, 21},
        {INFINITY, INFINITY, INFINITY, 20, 0, 26, INFINITY, 7, INFINITY},
        {11, INFINITY, INFINITY, INFINITY, 26, 0, 17, INFINITY, INFINITY},
        {INFINITY, 16, INFINITY, 24, INFINITY, 17, 0, 19, INFINITY},
        {INFINITY, INFINITY, INFINITY, 16, 7, INFINITY, 19, 0, INFINITY},
        {INFINITY, 12, 8, 21, INFINITY, INFINITY, INFINITY, INFINITY, 0}};
    G->adjMatrix = edgeMatrix;
    Prime(G);
    return 0;
}

克鲁斯卡尔(Kruskal)算法

实现步骤

1、从边入手,将图的所有边按照从小到大的顺序排列。
2、依次挑选出权值最小的边。
3、注意:要避免挑选的边和已经挑选出的边形成环,此时要舍弃当前挑选的边。
4、直到所有顶点都被选择并连通,则停止。

代码实现

/*
 * @Author: Xyh4ng
 * @Date: 2022-10-29 21:32:39
 * @LastEditors: Xyh4ng
 * @LastEditTime: 2022-10-29 22:13:26
 * @Description: 克鲁斯卡尔算法
 * Copyright (c) 2022 by Xyh4ng 503177404@qq.com, All Rights Reserved.
 */
#include <stdio.h>
#include <stdlib.h>

typedef struct Edge
{
    int begin;
    int end;
    int weight;
} Edge;

int FindEnd(int *adjvex, int b)
{
    while (adjvex[b] > 0)
    {
        b = adjvex[b];
    }
    return b;
}

void KrusKal(Edge G[])
{
    int adjvex[9] = {0}; // 用于记录那些边被选中(begin:index, end:value)
    // 遍历每一条边
    for (int i = 0; i < 15; i++)
    {
        int begin = FindEnd(adjvex, G[i].begin);
        int end = FindEnd(adjvex, G[i].end);
        if (begin != end) // 不会和已选边构成环
        {
            adjvex[begin] = end;
            printf("(%d, %d) %d\n", G[i].begin, G[i].end, G[i].weight);
        }
    }
}

int main()
{
    Edge G[15] = {{4, 7, 7},
                  {2, 8, 8},
                  {0, 1, 10},
                  {0, 5, 11},
                  {1, 8, 12},
                  {3, 7, 16},
                  {1, 6, 16},
                  {5, 6, 17},
                  {1, 2, 18},
                  {6, 7, 19},
                  {3, 4, 20},
                  {3, 8, 21},
                  {2, 3, 22},
                  {3, 6, 24},
                  {4, 5, 26}};
    KrusKal(G);
    return 0;
}

总结

对比两个算法,克鲁斯卡尔算法主要是针对边来展开,边数少时效率会非常高,所以对于稀疏图有很大的优势;而普里姆算法对于稠密图,即边数非常多的情况会更好一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值