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

news2024/11/25 14:39:47

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

(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 算法可能无法得到正确的最小生成树。代码中未对此进行处理。

建议:

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

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

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

相关文章

Stable Diffusion 是否使用 GPU?

在线工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 3D数字孪生场景编辑器 Stable Diffusion 已迅速成为最流行的生成式 AI 工具之一&#xff0c;用于通过文本到图像扩散模型创建图像。但是&#xff0c;它需…

软件外包开发的需求整理

提高软件需求描述的准确度是确保项目成功的关键一步。以下是一些建议&#xff0c;可以帮助提高需求描述的准确度&#xff0c;希望对大家有所帮助。 1.深入了解业务&#xff1a; 在开始编写需求之前&#xff0c;充分了解业务流程和业务目标。与业务团队密切合作&#xff0c;确保…

数字化转型时代,商业智能BI到底是什么?

据国际数据公司&#xff08;IDC&#xff09;预测&#xff0c;2025年时中国产生的数据量预计将达48.6ZB&#xff0c;在全球中的比例为27.8%。商业智能BI这一专为企业提供服务的数据类解决方案&#xff0c;仅2021年上半年在中国商业智能BI市场规模就达到了3.2亿美元&#xff0c;商…

idea生成代码(一):实现java语言的增删改查功能(基于EasyCode插件)支持自定义模板【非常简单】

idea生成代码&#xff08;一&#xff09;&#xff1a;实现java语言的增删改查功能&#xff08;基于EasyCode插件&#xff09;支持自定义模板【非常简单】 idea生成代码&#xff08;二&#xff09;&#xff1a;实现java语言的增删改查功能&#xff08;基于mybatis-plus代码生成器…

(七)Spring源码解析:Spring事务

对于事务来说&#xff0c;是我们平时在基于业务逻辑编码过程中不可或缺的一部分&#xff0c;它对于保证业务及数据逻辑原子性立下了汗马功劳。那么&#xff0c;我们基于Spring的声明式事务&#xff0c;可以方便我们对事务逻辑代码进行编写&#xff0c;那么在开篇的第一部分&…

Banana Pi BPI-M5 Boot Log 导出说明

准备&#xff1a; Preparation: 1、 一块bpi的开发板&#xff0c;一根ttl的串口线&#xff0c;以及一张烧录好镜像的sd/tf卡&#xff08;烧录到eMMC也行&#xff09;。 1. A BPI development board, a TTL serial port cable, and an SD/TF card with a burned image (it ca…

基于ssm的学生档案管理系统(有报告)。Javaee项目,ssm项目。

演示视频&#xff1a; 基于ssm的学生档案管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm项目。 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 项目介绍&#xff…

海外ASO优化之谷歌商店的评论优化

应用商店中的评分和评论&#xff0c;显示我们的应用程序的受欢迎程度以及用户对该应用程序的看法。评分和评论是以前或者是现在的用户分享的经验和公开的反馈。 1、提高应用评分评论。 高评分的应用可以从应用商店内的搜索流量中获得更多的点击量&#xff0c;通过推荐和推荐获…

基于单片机智能浇花系统仿真设计

**单片机设计介绍&#xff0c; 基于单片机智能浇花系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的智能浇花系统可以实现自动化浇水、测土湿度和温度等功能&#xff0c;以下是一个基本的仿真设计步骤&am…

CRM销售管理软件哪个好,该如何选择?(一)

销售团队对于任何一家企业来说都是重中之重&#xff0c;因此我们说一款可以辅助销售人员维护好客户的工具是企业发展的刚需。那么CRM销售管理软件哪个好&#xff0c;该如何选择&#xff0c;从从哪里方面去入手&#xff1f;来看看这两点吧&#xff1a; 功能方面 完整的功能可以…

ARPG----C++学习记录05 Section10 碰撞,重叠事件

collision碰撞 Query only 仅查询。包括请求&#xff0c;扫描和重叠&#xff0c;扫描两个物体知否存在重叠Physics Only 仅物理。重力&#xff0c;反弹等物理计算都开启 按下“~”输入show collision可以查看碰撞 给石块添加碰撞&#xff0c;Query可以阻挡人物过去&#xff0c;…

提升自动化测试:Apifox 产品更新全解析!

Apifox 新版本上线啦&#xff01; 看看本次版本更新主要涵盖的重点内容&#xff0c;有没有你所关注的功能特性&#xff1a; 自动化测试 新增 ForEach 循环组件数据库连接支持 MongoDB前/后置操作模块能力升级 支持使用 pm.executeAsync 异步执行外部程序支持自定义外部程序的…

【原创分享】DC-DC电源PCB设计要点

DC-DC电源是一种用于将直流&#xff08;DC&#xff09;电压转换为不同电压级别的电源。它通过内部的电路和拓扑结构&#xff0c;将输入电压调整为所需的输出电压&#xff0c;并提供稳定的电力供应。 DC-DC电源通常包括输入端子、输出端子、开关元件&#xff08;如开关管&#…

人工智能基础_机器学习027_L2正则化_岭回归_非稀疏性_原理解读_公式推导---人工智能工作笔记0067

然后我们再来看一下岭回归,也就是第二范数对吧, 他的公式,平方以后,加和然后开平方.L2的公式是 可以看到L2公式,也是有个阿尔法,惩罚项对吧. 可以看到因为L2带有平方,所以他的图形是个圆形 我们可以把L2范数,进行画出来看看 这里我们先看L2的公式,这里我们让 这个公式写成1 …

第四章mlp

生成数据集 读取数据集 data.TensorDataset(*data_arrays)mlp训练 loss nn.CrossEntropyLoss(reductionnone)我要掌握所有人脖颈上的绳 权重衰减 简单概述就是在标准意义的loss函数&#xff08;label值和计算值的差别&#xff09;中再加上一个 惩罚项&#xff0c;为什么要…

Java实现深拷贝的方式

文章目录 1. 实现 Cloneable 接口并重写 clone() 方法2. 使用序列化和反序列化实现深拷贝3. 第三方工具(1) Apache Commons BeanUtils 库(2) Apache Commons Lang 库(3) Spring Framework(4) Kryo 序列化库(5) FST 序列化库 1. 实现 Cloneable 接口并重写 clone() 方法 在 Jav…

科研绘图与学术图表绘制:从入门到精通

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 一、入门篇 1.1 软件介…

云原生Kubernetes系列 | 通过容器互联搭建wordpress博客系统

云原生Kubernetes系列 | 通过容器互联搭建wordpress博客系统 通过容器互联搭建一个wordpress博客系统。wordpress系统是需要连接到数据库上的&#xff0c;所以wordpress和mysql的镜像都是需要的。wordpress在创建过程中需要指定一些参数。创建mysql容器时需要把mysql的数据保存…

(论文阅读31/100)Stacked hourglass networks for human pose estimation

31.文献阅读笔记 简介 题目 Stacked hourglass networks for human pose estimation 作者 Alejandro Newell, Kaiyu Yang, and Jia Deng, ECCV, 2016. 原文链接 https://arxiv.org/pdf/1603.06937.pdf 关键词 Human Pose Estimation 研究问题 CNN运用于Human Pose E…