图的最小生成树-prim算法

  • 目的
  1. 了解最小生成树的概念
  2. 熟悉并掌握prim算法

  • 要求

一幅图共有6个结点,其值为字符A~F,构成如下图所示网络,

                                             

                                                             图1.实验要求的图

编程实现以下功能:1、采用邻接矩阵进行存储,2、采用prim算法,从顶点A出发生成最小生成树,依次输出所有选取的边,比如选取权值为6的边时,输出(A,B)。

  • 原理

1.最小代价生成树背景:假设在n个城市之间建立铁路网,在每两个城市之间都可设置一条线路,每条线路对应要付出的代价不同,n个城市之间最多可生成(n2-n)/2条线路,我们如何设计线路才能用最少的路径(最少要n-1条)最低的成本架设完这些铁路线路?

我们用最少的线路,即n-1条线路来贯穿n个城市,结果不唯一,付出的总代价也不唯一,我们将每一个结果称作生成树,而总花费最少的我们称作最小代价生成树(Minimum Cost Spanning Tree),简称最小生成树。

2.MST性质及理解

MST性质定义:设G=(V,E)是一个连通网,U是顶点集V的一个真子集。若(u,v)是G中一条具有最小权值的边,其中u∈U,v∈V-U,则一定存在G的一棵最小生成树包括此边(u,v)。

直白的意思就是说:整个图(网)G中权值最小的边一定会是最小生成树的边。

假设图G有3个顶点三条边,AB、AC和BC,权值分别为3、4、5,那么此时在这个简单图中的最小生成树肯定会包含权值为3的AB。因为A结点在选择AB或AC的时候会选权值小代价低的那个。其实这个MST也就是——贪心算法,选择当前情况下的最优解,即对应了MST中选择代价最小的。当顶点A在选择的时候,只能选择A-B或A-C,那么A-B的代价更小,故选择A-B。这就是MST性质。

图2.MST性质

3.Prim算法:Prim算法是一种用于解决最小生成树问题的贪心算法。最小生成树是指一个无向图的生成树(即包含所有顶点且边的权重之和最小的树)。Prim算法的基本思想是从一个起始顶点开始,逐步扩展生成树,每次选择权重最小的边连接一个新的顶点,直到生成树包含了图中的所有顶点。

Prim算法的时间复杂度为O(ElogV),其中V为顶点数,E为边数。它适用于稠密图,即边的数目接近V^2的情况。如果图是稀疏图,即边的数目接近V的情况,使用Kruskal算法可能更加高效。

                                                               图3.Prim算法图示

  • 编码步骤和结果

使用邻接矩阵来表示图,其中graph[i][j]表示顶点i到j的边的权值;primMST函数实现了Prim算法的核心,然后使用printMST函数用于打印最小生成树的边;最后main函数中首先读入图的顶点数和邻接矩阵,然后调用primMST函数来计算最小生成树。

其中Prim算法的具体实现步骤如下:

1. 创建一个空的最小生成树集合,开始时只包含起始顶点。

2. 初始化一个优先队列,用于选择权重最小的边。

3. 从起始顶点开始,将所有与之相邻的边加入优先队列。

4. 重复以下步骤,直到最小生成树包含了图中的所有顶点:

   - 从优先队列中选择权重最小的边,将其加入最小生成树集合。

   - 将该边连接的顶点标记为已访问。

   - 将该顶点的所有未访问的相邻边加入优先队列。

5. 最终得到的最小生成树就是原图的最小生成树。

图4.实验结果

  • 编码中出现的问题及解决方法

问题:输出的边的表示是由数字0-5来表示而不是用字符A-F

解决方法:使用一个字符数组来存储顶点的标签,

 char labels[] = {'A', 'B', 'C', 'D', 'E', 'F'};

然后修改printMST函数中对应的顶点的索引,就可解决这一问题

  • 总结及体会

在完成Prim算法的代码编写后,我对算法的理解更加深入。通过将算法转化为C语言代码,我更加清晰地理解了Prim算法的核心思想和实现过程。在编写代码的过程中,我不仅学会了如何使用邻接矩阵来表示图,还掌握了如何使用数组和循环来实现算法的逻辑。此外,我还熟悉了如何通过硬编码读取数据,以及通过字符数组将顶点索引转换为对应的字符标签。通过这个练习,我提高了对Prim算法的理解,同时也增强了对C语言编程的熟练度和实际应用能力。

  • 代码部分

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

#define MAX_VERTICES 6
#define
 INF 1000000

int graph[MAX_VERTICES][MAX_VERTICES]={
        {0,6,8,1000,1000,1000},
        {6,0,1000,12,14,1000},
        {8,1000,0,16,17,1000},
        {1000,12,16,0,1000,15},
        {1000,14,17,1000,0,5},
        {1000,1000,1000,15,5,0}
};
 // 图的邻接矩阵

int parent[MAX_VERTICES]; // 记录最小生成树的父节点
int key[MAX_VERTICES]; // 记录顶点与最小生成树的边的权值
bool mstSet[MAX_VERTICES]; // 记录顶点是否已经在最小生成树中

int minKey(int key[], bool mstSet[], int vertices) {
    int min = INF, min_index;
    for (int v = 0; v < vertices; v++) {
        if (mstSet[v] == false && key[v] < min) {
            min = key[v];
            min_index = v;
        }
    }
    return min_index;
}


void printMST(int vertices) {
    printf("Edge \t      Weight\n");
    char labels[]={'A','B','C','D','E','F'};
    for (int i = 1; i < vertices; i++) {
        printf("(%c , %c) \t%d \n", labels[parent[i]], labels[i], graph[i][parent[i]]);
    }
}



void primMST(int vertices) {
    for (int i = 0; i < vertices; i++) {
        key[i] = INF;
        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] == false && graph[u][v] < key[v]) {
                parent[v] = u;
                key[v] = graph[u][v];
            }
        }
    }
    printMST(vertices);
}


int main() {
    int vertices=6;
 
    primMST(vertices);
    return 0;
}

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

#define MAX_VERTICES 6
#define INF 1000000

int graph[MAX_VERTICES][MAX_VERTICES]={
        {0,6,8,1000,1000,1000},
        {6,0,1000,12,14,1000},
        {8,1000,0,16,17,1000},
        {1000,12,16,0,1000,15},
        {1000,14,17,1000,0,5},
        {1000,1000,1000,15,5,0}
}; // 图的邻接矩阵

int parent[MAX_VERTICES]; // 记录最小生成树的父节点
int key[MAX_VERTICES]; // 记录顶点与最小生成树的边的权值
bool mstSet[MAX_VERTICES]; // 记录顶点是否已经在最小生成树中

int minKey(int key[], bool mstSet[], int vertices) {
    int min = INF, min_index;
    for (int v = 0; v < vertices; v++) {
        if (mstSet[v] == false && key[v] < min) {
            min = key[v];
            min_index = v;
        }
    }
    return min_index;
}

void printMST(int vertices) {
    printf("Edge \t      Weight\n");
    char labels[]={'A','B','C','D','E','F'};
    for (int i = 1; i < vertices; i++) {
        printf("(%c , %c) \t%d \n", labels[parent[i]], labels[i], graph[i][parent[i]]);
    }
}


void primMST(int vertices) {
    for (int i = 0; i < vertices; i++) {
        key[i] = INF;
        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] == false && graph[u][v] < key[v]) {
                parent[v] = u;
                key[v] = graph[u][v];
            }
        }
    }
    printMST(vertices);
}

int main() {
    int vertices=6;
 
    primMST(vertices);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值