上机实验四 图的最小生成树算法设计 西安石油大学数据结构

实验名称:图的最小生成树算法设计

(1)实验目的:

掌握最小生成树算法,利用kruskal算法求解最小生成树。

(2)主要内容:

利用kruskal算法求一个图的最小生成树,设计Kruskal算法求解邻接矩阵存储结构下图的最小生成树的函数,并以下图为例设计一个主函数进行测试,要求输出最小生成树的各顶点及各边的权值。
在这里插入图片描述

边a-e的权值为1
边a-b的权值为3
边e-b的权值为4
边e-c的权值为6
边e-d的权值为7
边b-c的权值为5
边c-d的权值为2

知识储备

最小生成树(Minimum Spanning Tree,MST)是一种常见的图论算法,用于在一个加权连通图中寻找一棵生成树,使得树的所有边的权值之和最小。其中,Kruskal算法和Prim算法是两种常用的最小生成树算法。

  1. Kruskal算法

    • Kruskal算法是一种贪心算法,它通过将图中的所有边按权值从小到大进行排序,然后逐个考虑这些边,如果加入某条边不会构成环,则将其加入最小生成树中。这样直到最小生成树中包含了图中的所有顶点为止。
    • Kruskal算法适合于稀疏图,即顶点较多而边较少的图。
  2. Prim算法

    • Prim算法也是一种贪心算法,它从一个初始顶点开始,逐步长出最小生成树。在每一步中,选择与当前最小生成树相邻且权值最小的边所连接的顶点,并将该顶点加入最小生成树中。
    • Prim算法适合于稠密图,即顶点较少而边较多的图。

这两种算法都可以求解最小生成树,选择哪种算法取决于具体的应用场景和图的特点。在实际应用中,可以根据图的规模、边的数量、以及算法实现的难易程度等因素来选取合适的算法。

下面是使用C语言实现Kruskal算法来求解最小生成树的示例代码:

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

// 定义边的数据结构
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;
}

// 查找包含顶点v的子集的根节点
int find(int parent[], int v) {
    if (parent[v] == -1)
        return v;
    return find(parent, parent[v]);
}

// 将两个子集进行合并
void Union(int parent[], int x, int y) {
    int xroot = find(parent, x);
    int yroot = find(parent, y);
    parent[xroot] = yroot;
}

// 实现Kruskal算法
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]), [](const void* a, const void* b) {
        struct Edge* a1 = (struct Edge*)a;
        struct Edge* b1 = (struct Edge*)b;
        return a1->weight > b1->weight;
    });

    int *parent = (int*)malloc(V * sizeof(int));

    memset(parent, -1, V * sizeof(int));

    // 将边加入最小生成树中,直到最小生成树中有V-1条边
    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);
        }
    }

    // 打印最小生成树的边及权重
    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);
}

int main() {
    int V = 4; // 顶点数
    int E = 5; // 边数
    struct Graph* graph = createGraph(V, E);

    // 添加边的权重信息
    graph->edge[0].src = 0;
    graph->edge[0].dest = 1;
    graph->edge[0].weight = 10;

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

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

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

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

    // 使用Kruskal算法求解最小生成树
    kruskalMST(graph);

    return 0;
}

在这个示例中,我们定义了结构体Edge来表示边,并使用结构体Graph来表示图。在main函数中,我们创建了一个包含四个顶点和五条边的图,并使用Kruskal算法来求解最小生成树,然后打印出最小生成树的边及权重。

  • 这段C语言代码实现了Kruskal算法来求解最小生成树。让我来解释一下主要的步骤和代码逻辑:
    1. 首先定义了表示边的结构体Edge,包括边的起点、终点和权重;定义了表示图的结构体Graph,包括顶点数、边数和边的数组。
    1. 创建图的函数createGraph用于动态分配内存并初始化一个图的结构体。
    1. 实现了find函数和Union函数来进行并查集操作,用于判断两个顶点是否连通以及合并不同的连通分量。
    1. kruskalMST函数实现了Kruskal算法的核心逻辑。首先对所有边按照权重进行排序,然后遍历排序后的边数组,依次加入到最小生成树中,直到最小生成树中有V-1条边为止。在加入边的过程中,使用并查集来判断是否形成环路,从而保证最小生成树的连通性。
    1. main函数中创建一个包含四个顶点和五条边的图,并手动设置每条边的起点、终点和权重。然后调用kruskalMST函数求解最小生成树,并打印出最小生成树的边及权重。
      -总体来说,这段代码通过实现Kruskal算法的关键步骤,使用并查集来维护最小生成树的连通性,以及对边进行排序和筛选,最终得到了输入图的最小生成树。希望以上解释能够帮助你理解这段代码的实现原理。

以下是基于邻接矩阵存储结构的 Kruskal 算法的 C 语言代码,用于找到图的最小生成树。在这个例子中,我将使用您提供的图来测试最小生成树的算法。

问题描述

  1. 将图中的所有边按照权值从小到大进行排序。
  2. 依次遍历排序后的边,如果当前考虑的边连接的两个顶点不在同一个连通分量中,则将该边加入最小生成树中,并合并这两个顶点所在的连通分量。
  3. 重复步骤2,直到最小生成树中有n-1条边为止(n为顶点数)。

基本要求

  1. 输入:从标准输入或文件中读取图的顶点数、边数以及各条边的起点、终点和权值。
  2. 输出:将最小生成树的每条边及其权值输出到标准输出或文件中。
    正确性:确保程序能够正确地找到给定图的最小生成树。
  3. 效率:保证算法的时间复杂度在合理范围内,尽量提高程序的执行效率。

算法思想

Kruskal算法是一种用来寻找最小生成树的贪心算法。最小生成树是指在一个连通的无向图中找到一棵包含图中所有顶点的生成树,并且使得这棵树的边的权重之和尽可能小。

Kruskal算法的主要思想是通过不断地选择图中权重最小的边,并且保证不形成环路,逐步构建最小生成树。具体来说,算法按照边的权重从小到大的顺序进行处理,每次选择一条权重最小的边,如果这条边连接了两个不在同一个连通分量中的顶点,那么就将这条边加入最小生成树中,并且合并这两个连通分量,直到最小生成树中包含了图中所有的顶点为止。

模块划分

这段代码可以划分为几个功能模块:

  1. 数据结构定义模块:

    • 定义了表示图中边的数据结构 Edge;
    • 定义了表示整个图的数据结构 Graph。
  2. 并查集相关函数模块:

    • makeSet(int x):初始化并查集,将顶点x作为一个单独的集合;
    • findSet(int x):查找顶点x所在集合的根节点,采用路径压缩;
    • unionSet(int x, int y):合并包含顶点x和顶点y的两个集合。
  3. 辅助函数模块:

    • swap(Edge* a, Edge* b):交换两条边的函数;
    • sortEdges(Graph* graph):对图中的边按权重进行排序。
  4. Kruskal算法实现模块:

    • kruskal(Graph* graph):使用Kruskal算法寻找最小生成树。

在文件组织结构上,可以将这些函数放在同一个源文件中,比如 kruskal.c,然后在主程序文件中调用这些函数即可。当然,也可以根据需要将这些函数分别放在不同的文件中,并通过头文件来进行声明和引用。例如,可以创建 graph.hgraph.c 来存放数据结构定义和相关操作函数,以及 kruskal.hkruskal.c 来存放Kruskal算法实现相关的函数。

数据结构

边的数据结构 Edge:

用于表示图中的一条边,包括这条边连接的两个顶点(u, v)以及边的权重。
在代码中被定义为一个结构体,包括三个整型成员 u、v 和 weight。
图的数据结构 Graph:

用于表示整个图,包括图中顶点数 n、边数 m,以及边的具体信息。
在代码中被定义为一个结构体,包括两个整型成员 n 和 m,以及一个 Edge 类型的数组 edges。

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

#define MAX_EDGES 100
#define MAX_VERTICES 100

// 表示图中的一条边
typedef struct {
    int u, v, weight;  // 边的两个顶点及权重
} Edge;

// 表示整个图
typedef struct {
    int n, m;  // 图中顶点数和边数
    Edge edges[MAX_EDGES];  // 图中的边
} Graph;

int parent[MAX_VERTICES];  // 并查集的父节点数组

// 初始化并查集,将顶点x作为一个单独的集合
void makeSet(int x) {
    parent[x] = x;
}

// 查找顶点x所在集合的根节点,采用路径压缩
int findSet(int x) {
    while (x != parent[x]) {
        x = parent[x];
    }
    return x;
}

// 合并包含顶点x和顶点y的两个集合
void unionSet(int x, int y) {
    int rootX = findSet(x);
    int rootY = findSet(y);
    parent[rootX] = rootY;
}

// 交换两条边的函数
void swap(Edge* a, Edge* b) {
    Edge t = *a;
    *a = *b;
    *b = t;
}

// 对图中的边按权重进行排序
void sortEdges(Graph* graph) {
    int i, j;
    for (i = 0; i < graph->m - 1; i++) {
        for (j = 0; j < graph->m - i - 1; j++) {
            if (graph->edges[j].weight > graph->edges[j + 1].weight) {
                swap(&graph->edges[j], &graph->edges[j + 1]);
            }
        }
    }
}

// 使用Kruskal算法寻找最小生成树
void kruskal(Graph* graph) {
    int i, totalWeight = 0;
    for (i = 0; i < graph->n; i++) {
        makeSet(i);  // 初始化每个顶点为一个单独的集合
    }

    sortEdges(graph);  // 对边按权重进行排序

    printf("Minimum Spanning Tree:\n");
    for (i = 0; i < graph->m; i++) {
        int rootU = findSet(graph->edges[i].u);  // 查找边的两个顶点所在集合的根节点
        int rootV = findSet(graph->edges[i].v);
        if (rootU != rootV) {  // 如果两个顶点不在同一个集合中,说明加入这条边不会形成环路
            printf("Edge %c-%c: %d\n", 'a' + graph->edges[i].u, 'a' + graph->edges[i].v, graph->edges[i].weight);  // 输出该边
            totalWeight += graph->edges[i].weight;  // 累加总权重
            unionSet(rootU, rootV);  // 将这两个顶点所在集合合并
        }
    }
    printf("Total Weight: %d\n", totalWeight);  // 输出最小生成树的总权重
}

int main() {
    // 创建一个包含5个顶点和7条边的图
    Graph graph = {5, 7, {{0, 4, 1}, {0, 1, 3}, {4, 1, 4}, {4, 2, 6}, {4, 3, 7}, {1, 2, 5}, {2, 3, 2}}};
    kruskal(&graph);  // 寻找最小生成树
    return 0;
}

存在的问题和建议

可能存在的问题:

代码中未进行输入校验,如果输入的图不符合预期格式,可能会导致程序出错。
如果图中存在负权边,Kruskal 算法可能无法得到正确的最小生成树。代码中未对此进行处理。

建议:

可以添加输入校验的部分,确保输入的图符合预期格式。
对于负权边的情况,可以在算法中进行处理,或者在输入时进行限制。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

U-Mail海外邮件中继帮您解决企业邮件退信难题

过去一年&#xff0c;国内外形势严峻复杂&#xff0c;但中国外贸顶住压力、爬坡过坎&#xff0c;进出口规模冲破40万亿元大关&#xff0c;高达42万亿元人民币&#xff0c;中国连续6年位居货物贸易第一大国。随着我国疫情防控措进入新阶段&#xff0c;“拼经济”正在成为各地的一…

量化交易:使用 python 进行股票交易回测

执行环境: Google Colab 1. 下载数据 import yfinance as yfticker ZM df yf.download(ticker) df2. 数据预处理 df df.loc[2020-01-01:].copy()使用了 .loc 方法来选择索引为 ‘2020-01-01’ 以后的所有行数据。通过 .copy() 方法创建了一个这些数据的副本&#xff0c;确…

OpenGL_Learn11(光照)

目录 1. 光照 2. 环境光照 3. 漫反射光照 4. 代码实战 1. 光照 在OpenGL中主要分以下几个光照类型 环境光照(Ambient Lighting)&#xff1a;即使在黑暗的情况下&#xff0c;世界上通常也仍然有一些光亮&#xff08;月亮、远处的光&#xff09;&#xff0c;所以物体几乎永远不…

VScode+python开发,多个解释器切换问题

内容&#xff1a;主要VScode使用多个解释器 环境准备 VScode编辑器&#xff0c;两个版本python解释器 python3.7.2 python3.11.6 问题&#xff1a; 目前我们的电脑安装了python3.7.2、python3.11.6两个解释器&#xff0c;在vscode编辑器中&#xff0c;无法切换解释器使用如…

pytorch tensor数据类型转换为python数据

一、item() input: x torch.tensor([1.0]) x.item()output: 1.0二、tolist() input: a torch.randn(2, 2) a.tolist() a[0,0].tolist()output: [[0.012766935862600803, 0.5415473580360413],[-0.08909505605697632, 0.7729271650314331]]0.012766935862600803

FPGA UDP RGMII 千兆以太网(4)ARP ICMP UDP

1 以太网帧 1.1 1以太网帧格式 下图为以太网的帧格式: 前导码(Preamble):8 字节,连续 7 个 8’h55 加 1 个 8’hd5,表示一个帧的开始,用于双方 设备数据的同步。 目的 MAC 地址:6 字节,存放目的设备的物理地址,即 MAC 地址 源 MAC 地址:6 字节,存放发送端设备的…

星宿UI2.51资源付费变现小程序 支持流量主广告投放

目前&#xff0c;最新版的星宿UI是2.51版本。要搭建星宿UI&#xff0c;您需要准备备用域名、服务器和微信小程序账号。星宿UI提供了多项功能&#xff0c;包括文章展示、文章分类、资源链接下载和轮播图等。此外&#xff0c;还支持直接下载附件功能。这些功能使得星宿UI非常适合…

安防监控EasyCVR视频汇聚平台使用海康SDK播放出现花屏是什么原因?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、…

详解 KEIL C51 软件的使用·建立工程

单片机要运行,就必须将程序代码下载到程序存储器内部,但是在写进单片机之前要先将你写 的程序转换成*.hex 或*.bin 的文件.不同系列的单片机都有不同的软件对其进行编绎,而 keil Cx51 是德国开发的一个专为 51 系列单片机提供的软件开发平台,基本上现在的所有 51 系列内核的单片…

在 Electron上安装better-sqlite3出错

错误问题 一直卡npm install --global windows-build-tools --vs2015 这一步 解决 安装&#xff1a;pnpm install better-sqlite3 --save安装命令 pnpm i -D electron-rebuild 手动运行&#xff1a;node_modules/.bin/electron-rebuild -f -w better-sqlite3 我直接在packa…

后端接口性能优化分析-数据库优化

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…

246:vue+openlayers 绘制多边形,drawend获取最大幅宽

第246个 点击查看专栏目录 本示例是演示如何在vue+openlayers项目中绘制多边形,drawend获取最大幅宽。这里利用turf的turf.distance和openlayers的getExtent获取坐标值。 距离赤道越近,幅宽会越大一些,这里面利用了Math.abs来做绝对值的判断处理。 直接复制下面的 vue+open…

计算机毕设 机器学习股票大数据量化分析与预测系统 - python 计算机毕设

文章目录 0 前言1 课题背景2 实现效果UI界面设计web预测界面RSRS选股界面 3 软件架构4 工具介绍Flask框架MySQL数据库LSTM 5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业…

基于springboot实现体育场馆运营平台项目【项目源码】计算机毕业设计

基于springboot实现体育场馆运营平台演示 系统开发平台 在该数码论坛系统中&#xff0c;Eclipse能给用户提供更多的方便&#xff0c;其特点一是方便学习&#xff0c;方便快捷&#xff1b;二是有非常大的信息储存量&#xff0c;主要功能是用在对数据库中查询和编程。其功能有比…

Fourier分析导论——第5章——实数据R上的Fourier变换(E.M. Stein R. Shakarchi)

第5章 实数域ℝ上的Fourier变换 The theory of Fourier series and integrals has always had major difficulties and necessitated a large math- ematical apparatus in dealing with questions of con- vergence. It engendered the development of methods of summa…

PSP - 蛋白质复合物结构预测 Template 的 Multichain Mask 2D (二维多链掩码)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/134406459 在 蛋白质复合物结构预测 中&#xff0c;AlphaFold2 Multimer 的 Multichain Mask 2D 对于 模版特征 (Template) 的影响较大&#xff0…

创建具有负载平衡和集群的可扩展 Node.js 应用程序

创建具有负载平衡和集群的可扩展 Node.js 应用程序 负载平衡是提高应用程序性能、可扩展性和可用性的一项重要技术。当客户端向负载均衡器发出请求时&#xff0c;负载均衡器根据预定义的规则将请求分发到不同的实例。 可以使用cluster集群模块或 PM2 等工具根据负载均衡器的流…

若依前后分离版框架下Springboot java引入Mqtt接受发送消息

**这只是其中一种而且是粗浅的接、发消息。 同步机制还要跟搞物联网的同事沟通确认去看看能不能实现 或者是设备比较多的情况下 不会去使用同步机制 首先pom文件 引入依赖 ** <dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse…

​TechSmith Camtasia 2024破解版功能介绍及使用教程

在现在的网络互联网时代&#xff0c;越来越多的人走上了自媒体的道路。有些自媒体人会自己在网络上录制精彩视频&#xff0c;也有一些人会将精彩、热门的电影剪辑出来再加上自己给它的配音&#xff0c;做成大家喜欢看的电影剪辑片段。相信不管大家是自己平时有独特的爱好也好、…