Prim算法和Kruskal算法求最小生成树

一、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;
}
相关推荐
cwj&xyp10 分钟前
Python(二)str、list、tuple、dict、set
前端·python·算法
xiaoshiguang34 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡4 小时前
【C语言】判断回文
c语言·学习·算法
别NULL4 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇5 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
yuanbenshidiaos6 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习6 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA6 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo6 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc6 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法