【数据结构】最小生成树(Prim算法、Kruskal算法)解析+完整代码

news2024/9/20 20:50:24

5.1 最小生成树

  • 定义

    对一个带权连通无向图 G = ( V , E ) G=(V,E) G=(V,E),生成树不同,每棵树的权(即树中所有边上的权值之和)也可能不同。

    设R为G的所有生成树的集合,若T为R中边的权值之和最小的生成树,则T称为G的最小生成树(MST)。

  • 性质

    1.最小生成树可能有多个,但边的权值之和总是唯一且最小的;

    2.最小生成树的边数=顶点数-1。砍掉一条则不连通,增加一条会出现回路;

    3.如果一个连通图本身就是一棵树,则其最小生成树就是它本身;

    4.只有连通图才有最小生成树,非连通图只有生成森林。

5.1.1 Prim算法
  • 定义

    从某一个顶点开始构建生成树;

    每次将代价最小的新顶点纳入生成树,直到所有顶点都纳入为止。

    在这里插入图片描述

    • 即选最小权值的结点
  • 时间复杂度

    O ( ∣ V ∣ 2 ) O(|V|^2) O(V2),适用于稠密图(|E|大的)。

  • 算法的实现思想

    • 思路:

      V 0 V_0 V0开始,总共需要n-1轮处理。

      第一轮处理:循环遍历所有个结点,找到lowCast最低的,且还没加入树的顶点。

      再次循环遍历,更新还没加入的各个顶点的lowCast值。

    • 代码步骤:

      1.创建isJoin数组,初始为false,判断结点是否加入树。

      2.创建lowCost数组,用于存储到该结点的最短距离。

      3.从 v 0 v_0 v0开始,将与其连接的权值加入到lowCost数组中。

      4.遍历lowCast数组,找到最小值,将其加入树中,并继续遍历与其相连的边。

5.1.2 Kruskal算法
  • 定义

    每次选则一条权值最小的边,使这条边的两头连通(原本已经连通的不选),直到所有结点都连通。

    • 即每次选最小的边
  • 时间复杂度

    O ( ∣ E ∣ l o g 2 ∣ E ∣ ) O(|E|log_2|E|) O(Elog2E),适用于边稀疏图。

  • 算法的实现思想

    • 思路:

      初始:将各条边按权值排序。

      共执行e轮,每轮判断两个顶点是否属于同一集合,需要 O ( l o g 2 e ) O(log_2e) O(log2e)

5.1.3 最小生成树代码
A.邻接矩阵
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>

#define V 5 // 图的顶点数

// 找到距离集合最近的顶点
int min_key(int key[], bool mst_set[]) {
    int min = INT_MAX, min_index;
    for (int v = 0; v < V; v++) {
        if (mst_set[v] == false && key[v] < min) {
            min = key[v];
            min_index = v;
        }
    }
    return min_index;
}

// 打印最小生成树
void print_mst(int parent[], int graph[V][V]) {
    printf("Edge   Weight\n");
    for (int i = 1; i < V; i++)
        printf("%d - %d    %d \n", parent[i], i, graph[i][parent[i]]);
}

// Prim算法
void prim_mst(int graph[V][V]) {
    int parent[V]; // 存放最小生成树的父节点
    int lowCost[V];    // 用于存放顶点到最小生成树的最小权重
    bool isJoin[V]; // 记录顶点是否已经加入最小生成树

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

    lowCost[0] = 0; // 初始点为0
    parent[0] = -1; // 根节点没有父节点

    for (int count = 0; count < V - 1; count++) {
        int u = min_key(lowCost, isJoin);
        isJoin[u] = true;

        for (int v = 0; v < V; v++) {
            if (graph[u][v] && !isJoin[v] && graph[u][v] < lowCost[v]) {
                parent[v] = u;
                lowCost[v] = graph[u][v];
            }
        }
    }

    print_mst(parent, graph);
}

// Kruskal算法

// 结构体用于表示边
struct Edge {
    int src, dest, weight;
};

// 比较函数,用于排序
int compare(const void* a, const void* b) {
    return ((struct Edge*)a)->weight - ((struct Edge*)b)->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;
}

// Kruskal算法
void kruskal_mst(int graph[V][V]) {
    struct Edge result[V]; // 用于存放最小生成树的边
    int e = 0; // 表示result数组中的边数
    int i = 0; // 表示当前考虑的边

    // 边集合
    struct Edge edges[V*V];
    for (int u = 0; u < V; u++) {
        for (int v = u + 1; v < V; v++) {
            if (graph[u][v] != 0) {
                edges[e].src = u;
                edges[e].dest = v;
                edges[e].weight = graph[u][v];
                e++;
            }
        }
    }

    // 根据权重对边进行排序
    qsort(edges, e, sizeof(edges[0]), compare);

    int parent[V]; // 用于记录每个顶点的父节点
    for (int v = 0; v < V; v++)
        parent[v] = -1;

    // 最小生成树的边数小于V-1时继续
    while (i < V - 1 && e > 0) {
        struct Edge next_edge = edges[--e];

        // 检查是否会产生环
        int x = find(parent, next_edge.src);
        int y = find(parent, next_edge.dest);

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

    printf("Edge   Weight\n");
    for (int i = 0; i < V - 1; i++)
        printf("%d - %d    %d \n", result[i].src, result[i].dest, result[i].weight);
}

// 测试主函数
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}
    };

    printf("Prim's Minimum Spanning Tree:\n");
    prim_mst(graph);

    printf("\nKruskal's Minimum Spanning Tree:\n");
    kruskal_mst(graph);

    return 0;
}

在这里插入图片描述

B.邻接表
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>

#define MaxVertexNum 100
#define INF 9999

typedef struct ArcNode {
    int adjvex;
    int weight;
    struct ArcNode *next;
} ArcNode;

typedef struct VNode {
    int data;
    ArcNode *first;
} VNode, AdjList[MaxVertexNum];

typedef struct {
    AdjList vertices;
    int vexnum, arcnum;
} ALGraph;

void InitALGraph(ALGraph *G, int vexnum, int arcnum) {
    G->vexnum = vexnum;
    G->arcnum = arcnum;
    for (int i = 0; i < vexnum; i++) {
        G->vertices[i].data = i;
        G->vertices[i].first = NULL;
    }
}

void AddEdgeUndirectedALGraph(ALGraph *G, int v1, int v2, int weight) {
    ArcNode *arcNode1 = (ArcNode *)malloc(sizeof(ArcNode));
    arcNode1->adjvex = v2;
    arcNode1->weight = weight;
    arcNode1->next = G->vertices[v1].first;
    G->vertices[v1].first = arcNode1;

    ArcNode *arcNode2 = (ArcNode *)malloc(sizeof(ArcNode));
    arcNode2->adjvex = v1;
    arcNode2->weight = weight;
    arcNode2->next = G->vertices[v2].first;
    G->vertices[v2].first = arcNode2;
}

void PrintALGraph(ALGraph G) {
    for (int i = 0; i < G.vexnum; i++) {
        printf("%d -> ", G.vertices[i].data);
        ArcNode *p = G.vertices[i].first;
        while (p != NULL) {
            printf("(%d, %d) ", p->adjvex, p->weight);
            p = p->next;
        }
        printf("\n");
    }
}

// Prim算法
void Prim(ALGraph G) {
    int lowCost[G.vexnum], parent[G.vexnum];
    bool inMST[G.vexnum];

    for (int i = 0; i < G.vexnum; i++) {
        lowCost[i] = INF;
        parent[i] = -1;
        inMST[i] = false;
    }

    lowCost[0] = 0;

    for (int i = 0; i < G.vexnum - 1; i++) {
        int minIndex, minCost = INF;
        for (int j = 0; j < G.vexnum; j++) {
            if (!inMST[j] && lowCost[j] < minCost) {
                minCost = lowCost[j];
                minIndex = j;
            }
        }

        inMST[minIndex] = true;

        ArcNode *p = G.vertices[minIndex].first;
        while (p != NULL) {
            if (!inMST[p->adjvex] && p->weight < lowCost[p->adjvex]) {
                lowCost[p->adjvex] = p->weight;
                parent[p->adjvex] = minIndex;
            }
            p = p->next;
        }
    }

    printf("Edge   Weight\n");
    for (int i = 1; i < G.vexnum; i++) {
        printf("%d - %d    %d\n", parent[i], i, lowCost[i]);
    }
}

// Kruskal算法
typedef struct {
    int src, dest, weight;
} Edge;

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) {
    return ((Edge *)a)->weight - ((Edge *)b)->weight;
}

void Kruskal(ALGraph G) {
    Edge result[G.arcnum];
    Edge edges[G.arcnum];
    int parent[G.vexnum];

    int e = 0;
    for (int i = 0; i < G.vexnum; i++) {
        ArcNode *p = G.vertices[i].first;
        while (p != NULL) {
            if (i < p->adjvex) {
                edges[e].src = i;
                edges[e].dest = p->adjvex;
                edges[e].weight = p->weight;
                e++;
            }
            p = p->next;
        }
    }

    qsort(edges, G.arcnum, sizeof(Edge), compare);

    for (int i = 0; i < G.vexnum; i++)
        parent[i] = -1;

    int i = 0, j = 0;
    while (i < G.vexnum - 1 && j < G.arcnum) {
        Edge next_edge = edges[j++];

        int x = find(parent, next_edge.src);
        int y = find(parent, next_edge.dest);

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

    printf("Edge   Weight\n");
    for (int i = 0; i < G.vexnum - 1; i++) {
        printf("%d - %d    %d\n", result[i].src, result[i].dest, result[i].weight);
    }
}

int main() {
    ALGraph G;
    InitALGraph(&G, 5, 7);
    AddEdgeUndirectedALGraph(&G, 0, 1, 2);
    AddEdgeUndirectedALGraph(&G, 0, 3, 6);
    AddEdgeUndirectedALGraph(&G, 1, 2, 3);
    AddEdgeUndirectedALGraph(&G, 1, 3, 8);
    AddEdgeUndirectedALGraph(&G, 1, 4, 5);
    AddEdgeUndirectedALGraph(&G, 2, 4, 7);
    AddEdgeUndirectedALGraph(&G, 3, 4, 9);
    PrintALGraph(G);

    printf("Prim's Minimum Spanning Tree:\n");
    Prim(G);

    printf("\nKruskal's Minimum Spanning Tree:\n");
    Kruskal(G);

    return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1631490.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenMM——教程学习(1)

如何从零开始做一个蛋白小分子动力学模拟 AmberTools将被用来生成输入文件&#xff0c;OpenMM 将被用来运行模拟&#xff0c;模拟平台为在线百度AI Stuio, 并使用GPU加速。 First thing’s first, 到PDB 蛋白数据库下载一需要模拟的靶点晶体&#xff0c;备用。 1. H web server…

告别人工校对烦恼,Kompas AI智能纠错一键搞定

在快节奏的工作环境中&#xff0c;撰写和校对公文是必不可少的环节。然而&#xff0c;传统的人工校对方式既耗时又容易出错&#xff0c;严重影响了工作效率和公文质量。在这里&#xff0c;我想向大家分享一款专业的校对助手——Kompas AI。它是一款采用先进的自然语言处理技术的…

分享:怎么做老阳分享的选品师项目比较赚钱

在当今的商业环境中&#xff0c;选品师项目逐渐成为了一个炙手可热的创业选择。老阳作为业内知名的选品师&#xff0c;其分享的经验和方法对于想要入行或提升业绩的选品师来说&#xff0c;无疑是宝贵的财富。那么&#xff0c;如何才能做好老阳分享的选品师项目&#xff0c;实现…

ssm088基于JAVA的汽车售票网站abo+vue

汽车售票网站的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对汽车售票信息管理混乱&#xff0c;出错率…

【Camera KMD ISP SubSystem笔记】Request 流转与Bubble机制

ISP中断类型 SOF: 一帧图像数据开始传输 EOF: 一帧图像数据传输完成 REG_UPDATE: ISP寄存器更新完成(每个reg group都有独立的这个中断) EPOCH: ISP某一行结尾(默认20)就会产生此中断 BUFFER DONE: 一帧图像数据ISP完全写到DDR了 管理Isp request的几个List st…

系统思考——与其一样,不如不同

感谢开放大学的持续邀请&#xff0c;为创新创业的学生提供赋能。昨天的主题是《创业核心&#xff1a;从内在优势到价值创造》。在近期参与的创业活动中&#xff0c;我注意到许多创业者在介绍自己的公司时常说&#xff1a;“我们是做***的&#xff0c;最大的优势是比其家便宜。”…

mapbox实现3D模型飞行

贴个群号 WebGIS学习交流群461555818&#xff0c;欢迎大家 效果图 其实这种移动的可视化效果&#xff0c;在二维和三维中实现的思路都是一样的&#xff0c;无非是规划好路径&#xff0c;用几个关键节点&#xff0c;然后插值计算中间的点用以平滑移动效果&#xff0c;实时的根…

ETL中双流合并和多流合并的区别

一、ETL工具 ETLCloud数据集成平台集实时数据集成和离线数据集成以及API发布为一体的数据集成平台。与其他开源数据集成工具相比&#xff0c;采用轻量化架构、具有更快的部署速度、更快的数据传输速度、更低的运维成本&#xff0c;同时支持多租户的团队协作能力&#xff0c;能…

IDM下载器安装cmd注册

一、下载注册 安装包去IDM官网下载最新的试用版即可 或者直达百度网盘下载&#xff08;担心被河蟹&#xff0c;放在txt中了&#xff09;包含IDM下载器安装包和注册软件 IDM下载器安装包和注册软件下载地址链接 https://download.csdn.net/download/qq_31237581/89215452 如果…

一个自卑的人怎么变得自信

一个自卑的人怎么变得自信 自卑感是一种常见的心理状态&#xff0c;它可能源于个人对自己能力、外貌、价值等方面的负面评价。自卑感不仅会影响一个人的情绪状态&#xff0c;还可能阻碍其在生活、学习和工作中的表现。然而&#xff0c;自信并非一蹴而就的品质&#xff0c;它需要…

Elsevier——投稿系统遇到bug时的解决方法

重要&#xff1a;找期刊客服&#xff01;&#xff01;&#xff01; 一、方法&#xff1a; 1. 点击进入与官方客服的对话 2. 按要求输入个人信息 3. 输入遇到的问题 比如&#xff1a; 主题&#xff1a;The Current Status is jammed. 详细描述&#xff1a;The Current State o…

Flask框架进阶-Flask流式输出和受访配置--纯净详解版

Flask流式输出&#x1f680; 在工作的项目当中遇到了一种情况&#xff0c;当前端页面需要对某个展示信息进行批量更新&#xff0c;如果直接将全部的数据算完之后&#xff0c;再返回更新&#xff0c;则会导致&#xff0c;前端点击刷新之后等待时间过长&#xff0c;开始考虑到用进…

ESP32-S3如何用socket通信

实验目的&#xff1a; 通过 Socket 编程实现 pyWiFi-ESP32-S3 与电脑服务器助手建立连接&#xff0c;相互收 发数据。 首先先来简单了解一下Socket 我们先来看看网络层级模型图&#xff0c;这是构成网络通信的基础&#xff1a; 我们看看 TCP/IP 模型的传输层和应用层&…

内网安全【1】——域信息收集/应用网络凭证/CS插件/Android/BloodHound

内容大纲&#xff1a; 概念名词&#xff1a; 局域网 &#xff08;自己家&#xff09; 工作组 &#xff08;网吧&#xff09; 内网域 &#xff08;公司&#xff09; 比如一家公司有1000台机器 运维人员去管理1000 不可能每台上去都进行软件的安装 环境的部署 密码的设置…

Vue+Echarts 实现中国地图和飞线效果

目录 实现效果准备 实现效果 在线预览&#xff1a;https://mouday.github.io/vue-demo/packages/china-map/dist/index.html 准备 高版本的echarts&#xff0c;不包含地图数据&#xff0c;需要自己下载到项目中 1、地图数据下载 https://datav.aliyun.com/portal/school/at…

大田场景下的路径检测论文汇总

文章目录 2020Visual Servoing-based Navigation for Monitoring Row-Crop Fields 2020 Visual Servoing-based Navigation for Monitoring Row-Crop Fields code: https://github.com/PRBonn/visual-crop-row-navigation 摘要&#xff1a; 自主导航是野外机器人执行精确农业…

vue-quill-editor富文本插件控制字数显示

最终效果 富文本编辑框&#xff0c;只统计内容&#xff0c;不包含标签以及样式&#xff0c;超出最大字数限制提示。 具体代码 html <div class"relative"><quillEditorv-model"form.nutriSuggestion"ref"myQuillEditor7":options&quo…

【Python数据库】MongoDB

文章目录 [toc]数据插入数据查询数据更新数据删除 个人主页&#xff1a;丷从心 系列专栏&#xff1a;Python数据库 学习指南&#xff1a;Python学习指南 数据插入 from pymongo import MongoClientdef insert_data():mongo_client MongoClient(hostlocalhost, port27017)co…

【网络原理】数据链路层 及 DNS域名系统

系列文章目录 【网络通信基础】网络中的常见基本概念 【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序&#xff08;万字博文&#xff09; 【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制&#xff08;CRC算法、MD5算法&#xff09; 【网络…

为什么使用ZigBee技术开发智能家居产品?

随着智能家居市场的蓬勃发展&#xff0c;各种智能设备层出不穷&#xff0c;其中Zigbee技术因其独特的优势在这些智能设备中得到了广泛应用。那么&#xff0c;zigbee技术究竟具备哪些令人瞩目的优势&#xff0c;为什么能够得到如此广泛的应用呢&#xff1f; 什么是Zigbee协议&am…