📜  prims 和 kruskal 算法 - C 编程语言(1)

📅  最后修改于: 2023-12-03 14:45:39.492000             🧑  作者: Mango

Prims 和 Kruskal 算法 - C 编程语言

简介

Prims 和 Kruskal 算法是两种常用于解决最小生成树问题的算法。它们都是图论中的经典算法,在网络设计、路径规划和数据聚类等领域具有广泛的应用。

  • Prims 算法通过贪心策略逐步构建最小生成树,从一个起始节点开始,每次选择连接已添加节点和未添加节点的最小权值边,直到所有节点都被连接为止。
  • Kruskal 算法通过将所有边按照权值从小到大进行排序,然后按照顺序逐步添加边,如果添加一条边会形成环路,则跳过该边,直到生成树中包含了所有节点为止。

本文将分别介绍 Prims 算法和 Kruskal 算法的实现,并使用 C 编程语言提供示例代码。

Prims 算法

Prims 算法的实现可以使用最小优先队列(Min-Heap)来选取最小权值边。以下是基于邻接矩阵的 Prims 算法的示例代码:

#include <stdbool.h>
#include <stdio.h>
#include <limits.h>

#define MAX_VERTICES 100

int minKey(int key[], bool mstSet[], int vertices)
{
    int min = INT_MAX, min_index;

    for (int v = 0; v < vertices; v++)
    {
        if (!mstSet[v] && key[v] < min)
        {
            min = key[v];
            min_index = v;
        }
    }

    return min_index;
}

void primsMST(int graph[MAX_VERTICES][MAX_VERTICES], int vertices)
{
    int parent[MAX_VERTICES];
    int key[MAX_VERTICES];
    bool mstSet[MAX_VERTICES];

    for (int i = 0; i < vertices; i++)
    {
        key[i] = INT_MAX;
        mstSet[i] = false;
    }

    key[0] = 0;
    parent[0] = -1;

    for (int count = 0; count < vertices - 1; count++)
    {
        int u = minKey(key, mstSet, vertices);
        mstSet[u] = true;

        for (int v = 0; v < vertices; v++)
        {
            if (graph[u][v] && !mstSet[v] && graph[u][v] < key[v])
            {
                parent[v] = u;
                key[v] = graph[u][v];
            }
        }
    }

    printf("Edge \tWeight\n");
    for (int i = 1; i < vertices; i++)
    {
        printf("%d - %d \t%d \n", parent[i], i, graph[i][parent[i]]);
    }
}

以上代码中,graph 是输入的图的邻接矩阵表示法,vertices 表示节点的数量。primsMST 函数将根据图 graph 计算并打印出最小生成树的边及其权值。

Kruskal 算法

Kruskal 算法的实现需要使用并查集(Disjoint Set)来判断是否会形成环路。以下是 Kruskal 算法的示例代码:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX_EDGES 100

// 边的结构体定义
struct Edge
{
    int src, dest, weight;
};

// 图的结构体定义
struct Graph
{
    int vertices, edges;
    struct Edge* edge;
};

struct Graph* createGraph(int vertices, int edges)
{
    struct Graph* graph = (struct Graph*) malloc(sizeof(struct Graph));
    graph->vertices = vertices;
    graph->edges = edges;
    graph->edge = (struct Edge*) malloc(edges * sizeof(struct Edge));
    return graph;
}

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;
}

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;
}

void kruskalMST(struct Graph* graph)
{
    int vertices = graph->vertices;
    struct Edge result[vertices];  // 最小生成树的边

    int i = 0;  // 循环变量,用于遍历排序后的所有边
    int e = 0;  // 循环变量,用于遍历结果数组 result[]

    qsort(graph->edge, graph->edges, sizeof(graph->edge[0]), compare);

    int* parent = (int*) malloc(vertices * sizeof(int));
    memset(parent, -1, sizeof(int) * vertices);

    while (e < vertices - 1 && i < graph->edges)
    {
        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);
        }
    }

    printf("Edge \tWeight\n");
    for (i = 0; i < e; i++)
    {
        printf("%d - %d \t%d \n", result[i].src, result[i].dest, result[i].weight);
    }
}

以上代码中,graph 是输入的图的边列表表示法,vertices 表示节点的数量,edges 表示边的数量。kruskalMST 函数将根据图 graph 计算并打印出最小生成树的边及其权值。

使用示例

你可以根据你的需要,将上述两个算法应用于自己的图数据中。以下是一个简单的使用示例:

#include <stdio.h>

int main()
{
    // Prims 算法示例
    int graphPrims[MAX_VERTICES][MAX_VERTICES] = {
        {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}
    };
    printf("Prims 算法\n");
    primsMST(graphPrims, MAX_VERTICES);

    // Kruskal 算法示例
    int vertices = 4;
    int edges = 5;
    struct Graph* graphKruskal = createGraph(vertices, edges);

    graphKruskal->edge[0].src = 0;
    graphKruskal->edge[0].dest = 1;
    graphKruskal->edge[0].weight = 10;

    graphKruskal->edge[1].src = 0;
    graphKruskal->edge[1].dest = 2;
    graphKruskal->edge[1].weight = 6;

    graphKruskal->edge[2].src = 0;
    graphKruskal->edge[2].dest = 3;
    graphKruskal->edge[2].weight = 5;

    graphKruskal->edge[3].src = 1;
    graphKruskal->edge[3].dest = 3;
    graphKruskal->edge[3].weight = 15;

    graphKruskal->edge[4].src = 2;
    graphKruskal->edge[4].dest = 3;
    graphKruskal->edge[4].weight = 4;

    printf("\nKruskal 算法\n");
    kruskalMST(graphKruskal);

    return 0;
}

请注意,以上示例代码是在静态的图上运行的,你可以根据自己的需求动态创建图并输入相关数据。

结论

Prims 算法和 Kruskal 算法是解决最小生成树问题的两种经典算法。它们在 C 编程语言中的实现可被灵活应用于各种需求场景。最小生成树问题在计算机科学和网络设计中是非常重要的,通过使用这两种算法,可以轻松地找到连接所有节点的最小成本路径。

希望本文能对你理解和实现 Prims 算法和 Kruskal 算法有所帮助!