最小生成树算法C语言代码实例

最小生成树算法C语言代码实例

什么是最小生成树?

最小生成树(MST)是指在一张图中,找到一颗包含所有节点的连通子树,且这颗树的边的权值之和最小。其中,连通子树是指子树中任意两点都可以互相到达的树。

Kruskal算法实现最小生成树

Kruskal算法的过程

Kruskal算法是一种贪心算法,它的基本思想是先将图中所有边按权值从小到大排序,然后从小到大地选择边,加入到一个新的集合中,如果加入这个边能够形成一个新的环路,则舍弃这个边,否则就保留这个边,直到加入的边数等于节点数减1为止。此时所构成的边即为最小生成树。

Kruskal算法的代码实现

下面是Kruskal算法的C语言代码实现,其中边的结构体定义如下:

struct Edge {
    int u, v; // 边的两个端点
    int w; // 边的权值
};

核心函数代码:

int cmp(const void* a, const void* b) {
    return ((struct Edge*)a)->w - ((struct Edge*)b)->w;
}

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 Kruskal(struct Edge edges[], int V, int E) {
    int parent[V];
    memset(parent, -1, sizeof(parent));
    int i = 0, j = 0, k = 0;
    struct Edge result[V];

    // 对所有边按权值从小到大排序
    qsort(edges, E, sizeof(edges[0]), cmp);

    while (j < V - 1 && k < E) {
        int u = edges[k].u;
        int v = edges[k].v;
        int w = edges[k].w;

        int x = find(parent, u);
        int y = find(parent, v);

        if (x != y) {
            result[i++] = edges[k];
            Union(parent, x, y);
            j++;
        }
        k++;
    }

    printf("Edges in MST:\n");
    for (i = 0; i < j; i++) {
        printf("%d - %d : %d\n", result[i].u, result[i].v, result[i].w);
    }
}

Prim算法实现最小生成树

Prim算法的过程

Prim算法也是一种贪心算法,它的基本思想是从某个顶点开始,不断地扩张当前生成树,每次选择当前生成树与其它未加入到生成树中的节点所对应的边中的最短边加入到生成树中,直到所有节点都加入到生成树中为止。此时所构成的边即为最小生成树。

Prim算法的代码实现

下面是Prim算法的C语言代码实现,其中边的结构体定义与之前一致:

struct Edge {
    int u, v; // 边的两个端点
    int w; // 边的权值
};

核心函数代码:

void Prim(int graph[V][V], int V) {
    int parent[V]; // 存放最小生成树的边
    int key[V]; // 存放顶点到最小生成树的最小边权值
    bool mstSet[V]; // 存放那些顶点已经包含在最小生成树中

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

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

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

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

    printf("Edges in MST:\n");
    for (int i = 1; i < V; i++) {
        printf("%d - %d : %d\n", parent[i], i, graph[i][parent[i]]);
    }
}

示例说明

假设一个图如下所示:

  5  ----- 4
 / \       | \
2   \      |  3
 \   \     | /
  6 - 1 --- 0
      9

按照Kruskal算法,首先将所有边按权值从小到大排序:

(2,6): 3
(0,1): 4
(7,8): 7
(3,4): 8
(6,1): 8
(1,4): 9
(3,5): 10
(1,2): 11

从最小的边开始加入到生成树中,一直到生成树中有5条边为止,这样形成的最小生成树为:

Edges in MST:
2 - 6 : 3
0 - 1 : 4
1 - 4 : 9
6 - 1 : 8

按照Prim算法,首先选择一个顶点,比如顶点0作为根节点,将所有顶点到根节点的最短距离存储在key数组中:

key[] = {0, INF, INF, INF, INF, INF, INF, INF, INF}

然后选择key数组中最短距离的顶点2,将它加入到生成树中,并更新key数组:

key[] = {0, INF, 3, INF, INF, INF, INF, INF, INF}

接下来,选择key数组中最短距离的顶点6,将它加入到生成树中,并更新key数组:

key[] = {0, 6, 3, INF, INF, 10, INF, INF, INF}

重复上述操作,直到生成树中包含所有的顶点,产生的最小生成树为:

Edges in MST:
0 - 1 : 4
1 - 2 : 11
2 - 6 : 3
1 - 4 : 9

总结

以上就是Kruskal算法和Prim算法实现最小生成树的完整攻略。其中,Kruskal算法的时间复杂度为O(ElogE),空间复杂度为O(E),Prim算法的时间复杂度为O(V^2)或O(ElogV),空间复杂度为O(V+E)。在实际使用时,根据图中节点的数量和边的数量来选择恰当的算法。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:最小生成树算法C语言代码实例 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • Golang中的错误处理的示例详解

    Golang中的错误处理的示例详解 为什么需要错误处理 在编程中,无论我们的语言是什么,都会遇到各种错误。为了避免出现错误后程序崩溃或者无法正常工作,我们需要考虑错误的处理方法。Golang官方鼓励使用错误来处理问题,而不是抛出异常或者在程序中使用错误的标记。因此,学习如何使用Golang来处理错误显得尤为必要。 错误类型 在Golang中,错误是一个内置接…

    C 2023年5月22日
    00
  • C++ 中回调函数详解及简单实例

    C++ 中回调函数详解及简单实例 什么是回调函数 在C++中,回调函数是一种以函数指针的形式实现的编程技巧。简而言之,回调函数就是一种通过在函数参数中传递函数指针的形式,来实现在需要时调用这个函数的一种方式。 回调函数的用途 回调函数最常见的使用场景是在异步和事件驱动编程中,当一个事件触发时,需要某个函数处理该事件。由于该事件的触发时间不确定,因此需要把该函…

    C 2023年5月30日
    00
  • C++中继承(inheritance)详解及其作用介绍

    C++中继承(inheritance)详解及其作用介绍 什么是继承? 继承是一种面向对象编程中的重要概念,指的是类(子类)拥有父类的属性和方法,在父类的基础上进行扩展或重写。继承关系中,父类也称为基类或超类,子类也称为派生类或衍生类。继承关系体现了面向对象编程中的一种复用机制,其中子类可以重用父类的代码,而且不需要重新写入相同的代码。 在C++中,继承关系可…

    C 2023年5月22日
    00
  • 进程

    进程、轻量级进程和线程 进程在教科书中通常定义:进程是程序执行时的一个实例,可以把它看作充分描述程序已经执行到何种程度的数据结构的汇集。 从内核的观点,进程的目的就是担当分配系统资源(CPU时间、内存等)的实体。   当一个进程被创建时,他几乎于父进程相同。它接受父进程地址空间的一个(逻辑)拷贝,并从进程创建系统调用的下一条指令开始执行于父进程相同的代码。尽…

    C 2023年4月27日
    00
  • C程序 将两个矩阵相加

    首先,写一个程序可以将两个矩阵相加,需要按照以下步骤进行: 定义两个矩阵,并初始化数据 定义一个结果矩阵 遍历两个矩阵,并将对应元素相加,然后存放到结果矩阵中 输出结果矩阵 下面是一个标准的C程序代码示例: #include <stdio.h> #define ROW 2 #define COL 2 void matrix_add(int mat…

    C 2023年5月9日
    00
  • C++实现图书管理系统简易版

    C++实现图书管理系统简易版攻略 前言 图书管理系统是一种基础的管理系统,它可以帮助管理员管理图书信息和读者信息,完成借阅、归还等基本操作。本文将详细介绍如何使用C++编程实现图书管理系统的简易版。 实现步骤 1. 确定需求 在编写代码之前,需要明确所要实现的功能需求。基本需求如下: 管理员可以添加图书和删除图书 管理员可以添加读者和删除读者 读者可以查询图…

    C 2023年5月24日
    00
  • Redis的数据存储及String类型的实现

    Redis是一款开源的高性能缓存系统,支持多种数据类型的存储,其中String类型是最简单的一种数据类型,并且使用最频繁。本文将从Redis的数据存储及String类型的实现两方面进行详细介绍。 Redis的数据存储 Redis的数据存储采用的是键值对的方式,其中键只能是字符串类型,值则可以是以下五种数据类型之一:String、List、Hash、Set、S…

    C 2023年5月22日
    00
  • C和C++如何实现互相调用详解

    C和C++之间可以通过C++的extern “C”特性来实现互相调用。C++允许在函数前加上extern “C”以指明该函数使用C风格的命名规则,这样可以保证C++编译器不会改变该函数的名字、参数个数或类型等信息。然后在C中就可以直接调用该函数了。 具体步骤如下: 在C++中声明需要在C中调用的函数时,在函数前加上extern “C”关键字,这将使得函数在编…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部