C数据结构与算法——无向图(最小生成树) 应用

news2025/1/12 22:52:46

实验任务

(1) 掌握Kruskal最小生成树算法;
(2) 掌握Prim最小生成树算法。

实验内容

(1) 随机生成一个无向网 G = ( V, E ),V = { A, B, C, D, E, F },| E | = 11,边的权值取值范围为 [ 1, 40 ];
(2) 使用Prim算法求出图G的最小生成树,给出选择顶点的顺序;
(3) 使用Kruskal算法从顶点A出发求图G的最小生成树,给出算法添加边的顺序;
(4) 给出最小生成树的代价。

实验源码

#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "math.h"

#define MAX_AMNUMS 100

int cost = 0; // 代价

// 枚举 布尔
typedef enum {
    FALSE,
    TRUE
} Boolean;

// 队列
typedef struct {
    int *base;
    int front;
    int rear;
} SqQueue;

// 无向网
typedef struct {
    char verxs[MAX_AMNUMS];
    int arcs[MAX_AMNUMS][MAX_AMNUMS];
    int numVertexes, numEdges;
    int minW, maxW;
} AMGraph;

// 访问标志
Boolean visited[MAX_AMNUMS];

// 克鲁斯卡尔
struct { // 辅助数组 Edge(边集) 的定义
    char Head; // 边的始点
    char Tail; // 边的终点
    int lowCost; // 边上的权值
} Edge[MAX_AMNUMS];

// 辅助数组 VexSet 的定义
int VexSet[MAX_AMNUMS];

Boolean InitUDN(AMGraph *G); // 初始化
void CreateRandUDN(AMGraph *G); // 创建随机无向网
void knuthShuffle(int arr[], int length); // 洗牌算法
void swapInt(int *card_1, int *card_2); // 交换函数
void PrintUDN(AMGraph G); // 打印无向网
void MiniSpanTree_Prim(AMGraph G, char vex); // 普里姆最小生成树算法
void MiniSpanTree_Kruskal(AMGraph G); // 克鲁斯卡尔最小生成树算法
void BubbleSort(int length); // 冒泡排序
int LocateVex(AMGraph G, char vex); // 取出顶点信息对应的顶点下标
void PrintCost(); // 打印代价

/**
 * <h2>无向网 实验三</h2>
 * @return 0
 */
int main() {

    srand(time(NULL));
    // 定义
    AMGraph G;
    // 初始化
    InitUDN(&G);
    // 顶点数+边数
    G.numVertexes = 7;
    G.numEdges = 11;
    // 权值范围
    G.minW = 1;
    G.maxW = 40;
    // 创建随机无向网
    CreateRandUDN(&G);
    // 打印无向网
    PrintUDN(G);
    printf("\n");
    // Prim算法
    printf("\n使用Prim算法从顶点A出发求G的最小生成树,依次选择的顶点是:\n");
    MiniSpanTree_Prim(G, 'A');
    printf("\n");
    // Kruskal算法
    printf("\n使用Kruskal算法求G的最小生成树,依次选择的边是:\n");
    MiniSpanTree_Kruskal(G);
    printf("\n");
    // 最小生成树代价
    printf("\n图G最小生成树的代价为:");
    PrintCost();

    getchar();
}

// 初始化无向网
Boolean InitUDN(AMGraph *G) {
    G = (AMGraph *) malloc(sizeof(AMGraph));
    if (!G) {
        return FALSE;
    }
    return TRUE;
}

void CreateRandUDN(AMGraph *G) {
    // 顶点编号(字母)
    for (int i = 0; i < G->numVertexes; i++) {
        G->verxs[i] = 'A' + i;
    }
    // 初始化 邻接矩阵为 相对[minW-maxW]范围无穷大
    for (int i = 0; i < G->numVertexes; i++) {
        for (int j = 0; j < G->numVertexes; j++) {
            G->arcs[i][j] = (G->maxW) + 1;
        }
    }
    /*
     * 随机生成 无向网上三角部分
     */
/*    // 结果测试
    int arr[6][6] = {
            {41, 9,  41, 41, 17, 13},
            {9,  41, 19, 16, 7,  41},
            {41, 19, 41, 32, 41, 12},
            {41, 16, 32, 41, 34, 14},
            {17, 7,  41, 34, 41, 22},
            {13, 41, 12, 14, 22, 41}
    };
    for (int i = 0; i < 6; i++) {
        for (int j = 0; j < 6; j++) {
            G->arcs[i][j] = arr[i][j];
        }
    }*/
    int upNum = (pow(G->numVertexes, 2) - G->numVertexes) / 2;
    int randArr[upNum];
    // 生成权值
    for (int i = 0; i < G->numEdges; i++) {
        // max-min+1 , min
        randArr[i] = rand() % (G->maxW) + (G->minW);
    }
    // 生成 无权值
    for (int i = (G->numEdges); i < upNum; i++) {
        randArr[i] = (G->maxW) + 1;
    }
    // 洗牌
    knuthShuffle(randArr, upNum);
    // 放入无向网(对称赋值)
    int count = 0;
    for (int i = 0; i < G->numVertexes - 1; i++) {
        for (int j = i + 1; j < G->numVertexes; j++) {
            G->arcs[i][j] = G->arcs[j][i] = randArr[count++];
        }
    }
}

// 洗牌
void knuthShuffle(int arr[], int length) {
    for (int i = length - 1; i >= 1; i--) {
        swapInt(&arr[i], &arr[rand() % (i + 1)]);
    }
}

// 交换
void swapInt(int *card_1, int *card_2) {
    int tCard;
    tCard = *card_1;
    *card_1 = *card_2;
    *card_2 = tCard;
}

// 输出
void PrintUDN(AMGraph G) {
    // 其他信息
    printf("无向网 G =(V, E)的顶点集 V = {");
    for (int i = 0; i < G.numVertexes; i++) {
        printf(" %c", G.verxs[i]);
        if (i != (G.numVertexes - 1)) {
            printf(",");
        }
    }
    printf(" }、边集 E及其权值如下邻接矩阵所示:\n");
    // 表头
    printf("    _");
    for (int i = 0; i < G.numVertexes; i++) {
        printf("  %c", G.verxs[i]);
    }
    printf(" _");
    printf("\n");
    // 内容
    for (int i = 0; i < G.numVertexes; i++) {
        // 最左侧打印
        if (i == (G.numVertexes - 1)) {
            printf(" %c |_", G.verxs[i]); // 尾行
        } else {
            printf(" %c | ", G.verxs[i]); // 非尾行
        }
        // 中间打印
        for (int j = 0; j < G.numVertexes; j++) {
            if (G.arcs[i][j] > G.maxW) {
                printf(" ∞");
            } else {
                printf(" %2d", G.arcs[i][j]);
            }
        }
        // 最右侧打印
        if (i == (G.numVertexes - 1)) {
            printf(" _|"); // 尾行
        } else {
            printf("  |"); // 非尾行
        }
        printf("\n");
    }
}

// Prim
void MiniSpanTree_Prim(AMGraph G, char vex) {
    char adjVex[MAX_AMNUMS]; // 最小边在 v 中那个顶点
    int lowCost[MAX_AMNUMS]; // 最小边上的权值

    int v = LocateVex(G, vex); // v 为顶点vex的下标
    /*
     * 初始化
     */
    for (int i = 0; i < G.numVertexes; i++) { // 对 VEX-V 的每一个顶点 Vi, 初始化adjVex 和 lowCost
        if (i != v) {
            adjVex[i] = vex; // 初始化全部先为 v 的顶点信息
            lowCost[i] = G.arcs[v][i]; // 将邻接矩阵第 0行 所有权值 先加入 lowCost
        }
    }
    lowCost[v] = G.minW - 1; // 初始,V = { v }
    /*
     * 构造最小生成树
     */
    for (int i = 1; i < G.numVertexes; i++) { // 选择其余 n-1 个顶点,生成 n-1 条边(n=G.numVertexes)
        // 求出T的下一个结点:第 k 个顶点,closedge[k]中存有当前最小边
        int min = 41;
        int j = 0;
        while (j < G.numVertexes) {
            // 寻找 和 最小边 和 最小边的另一个顶点
            if (lowCost[j] != (G.minW - 1) && lowCost[j] < min) {
                min = lowCost[j];
                v = j; // 当前顶点 变为 相邻顶点
            }
            j++;
        }
        // 输出
        char tVex_L = adjVex[v]; // tVex_L 为最小边的一个顶点,tVex 属于 VEX
        char tVex_R = G.verxs[v]; // tVex_R 为最小边的另一个顶点,属于 VEX - V
//        printf("(%c, %c)", tVex_L, tVex_R);
        if (i == 1) {
            printf("%c", tVex_L);
        }
        printf(" -> %c", tVex_R);
        // 选择最小边
        lowCost[v] = G.minW - 1; // 第 v 个顶点并入 V 集
        for (int k = 0; k < G.numVertexes; k++) {
            if (lowCost[k] != (G.minW - 1) && G.arcs[v][k] < lowCost[k]) { // 新顶点并入 V 后重新选择最小边
                adjVex[k] = G.verxs[v]; // 存入新顶点 v 的信息
                lowCost[k] = G.arcs[v][k];
            }
        }
    }
}

// Kruskal
void MiniSpanTree_Kruskal(AMGraph G) {
    // 取出无向网的权值 到辅助数组 Edge[]中
    int length = 0;
    for (int i = 0; i < G.numVertexes - 1; i++) {
        for (int j = i + 1; j < G.numVertexes; j++) {
            if (G.arcs[i][j] <= G.maxW) {
                Edge[length].Head = G.verxs[i];
                Edge[length].Tail = G.verxs[j];
                Edge[length++].lowCost = G.arcs[i][j];
            }
        }
    }
    // 将数组中的 所有元素 按 权值 从小到大排序
    BubbleSort(length);
    // 辅助数组,表示各顶点自成一个连通分量
    for (int i = 0; i < G.numVertexes; i++) {
        VexSet[i] = i;
    }
    // 依次查看数组 Edge 中的边
    for (int i = 0; i < G.numEdges; i++) {
        int vHead = LocateVex(G, Edge[i].Head); // vHead 为边的始点 Head 的下标
        int vTail = LocateVex(G, Edge[i].Tail); // vTail 为边的终点 Tail 的下标
        int vsHead = VexSet[vHead]; // 获取边 Edge[i]的始点所在的连通分量 vsHead
        int vsTail = VexSet[vTail]; // 获取边 Edge[i]的终点所在的连通分量 vsTail
        if (vsHead != vsTail) { // 边的两个顶点分别属于不同的连通分量
            if (i != 0) {
                printf(" -> ");
            }
            printf("(%c, %c)", Edge[i].Head, Edge[i].Tail); // 输出此边
            cost += Edge[i].lowCost;
            for (int j = 0; j < G.numVertexes; j++) { // 合并 vsHead 和 vsTail两个分量,即两个集合统一编号
                if (VexSet[j] == vsTail) { // 集合编号为 vsTail 的都改为 vsHead
                    VexSet[j] = vsHead;
                }
            }
        }
    }
}

// 排序(小到大)
void BubbleSort(int length) {
    int temp;
    char tHead;
    char tTail;
    for (int i = 0; i < length - 1; i++) { // 外层循环:轮次
        int index = -1;
        for (int j = 0; j < length - 1 - i; j++) { // 内层循环:比较并交换位置(找出每轮最小数)
            if (Edge[j].lowCost > Edge[j + 1].lowCost) {
                temp = Edge[j + 1].lowCost;
                tHead = Edge[j + 1].Head;
                tTail = Edge[j + 1].Tail;
                Edge[j + 1].lowCost = Edge[j].lowCost;
                Edge[j + 1].Head = Edge[j].Head;
                Edge[j + 1].Tail = Edge[j].Tail;
                Edge[j].lowCost = temp;
                Edge[j].Head = tHead;
                Edge[j].Tail = tTail;
                index++;
            }
        }
        if (index == -1) {
            break; // 为提高排序效率,如果在每轮排序中未发生一次位置交换则代表已经是需要的顺序(直接跳出排序)
        }
    }
}

// 取顶点下标
int LocateVex(AMGraph G, char vex) {
    for (int i = 0; i < G.numVertexes; i++) {
        if (G.verxs[i] == vex) {
            return G.verxs[i] - 'A';
        }
    }
    return FALSE;
}

// 输出代价
void PrintCost() {
    printf("%d ", cost);
}

实验结果

在这里插入图片描述

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

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

相关文章

离散化思想——只处理有效数据的优化思想

离散化思想——只处理有效数据的优化思想 什么是离散化离散化题目——校门外的树&#xff08;超强版&#xff0c;1e9&#xff09;题目描述输入格式输出格式样例样例输入样例输出 提示 思路分析朴素做法离散化&#xff01;&#xff01;代码分析数组循环 什么是离散化 离散化思想…

C语言之浮点数_数据存储篇(2)

目录 浮点数 什么是浮点数呢&#xff1f; 为什么叫浮点数&#xff1f; 浮点数家族 浮点数表示的范围&#xff1f; 浮点数存储的例子 浮点数的存储方式 写成规定形式是怎样的&#xff1f; 那SME在内存中如何分配的呢&#xff1f; 为什么要这样存储&#xff1f; 浮点…

44、TCP报文(二)

接上节内容&#xff0c;本节我们继续TCP报文首部字段含义的学习。上节为止我们学习到“数据偏移”和“保留”字段。接下来我们学习后面的一些字段&#xff08;暂不包含“检验和”的计算方法和选项字段&#xff09;。 TCP首部结构&#xff08;续&#xff09; “数据偏移”和“保…

人工智能在车牌识别中的应用与影响

引言&#xff1a;车牌识别技术是基于人工智能的一种重要应用&#xff0c;通过对监控视频中的车辆图像进行处理和分析&#xff0c;可以快速、准确地识别车牌号码。这项技术的广泛应用可以帮助交通管理、停车场管理&#xff0c;甚至追踪犯罪嫌疑人的车辆。本文将详细探讨车牌识别…

从LeakCanary看ViewModel生命周期监控

前面两篇文章中已经了解了LeakCanary中Service和Fragment生命周期监控的实现&#xff0c;那么ViewModel生命周期监控又是怎么实现的呢&#xff1f; 同样的&#xff0c;要了解ViewModel生命周期监控&#xff0c;我们首先应该清楚在代码结构中ViewModel是如何存储获取的&#xf…

【零基础自用】理解python为什么要用虚拟环境

不知道学过MATLAB或者R的小伙伴刚刚接触python的时候会不会被各种python版本&#xff0c;包版本&#xff0c;虚拟环境之类的搞的头晕眼花。 问题一 包版本 先来假设&#xff0c;我们自己开发了一个包MyPackage 1.0&#xff0c;里面包含一个模块叫PreTrained&#xff0c;然后去…

Python爬虫(十三)_案例:使用XPath的爬虫

本篇是使用XPath的案例 案例&#xff1a;使用XPath的爬虫 现在我们用XPath来做一个简单的爬虫&#xff0c;我们尝试爬取某个贴吧里的所有帖子且将该帖子里每个楼层发布的图片下载到本地。 #-*- coding:utf-8 -*- #tieba_xpath.py"""作用&#xff1a;本案例使用…

【AGC】Publishing api怎么上传绿色认证审核材料

【问题描述】 华为应用市场会对绿色应用标上特有的绿色标识&#xff0c;代表其通过华为终端开放实验室DevEco云测平台的兼容性、稳定性、安全、功耗和性能的检测和认证&#xff0c;是应用高品质的象征。想要自己的应用认证为绿色应用就需要在发布应用时提供绿色认证审核材料&a…

Go语言基础之基本数据类型

Go语言中有丰富的数据类型&#xff0c;除了基本的整型、浮点型、布尔型、字符串外&#xff0c;还有数组、切片、结构体、函数、map、通道&#xff08;channel&#xff09;等。Go 语言的基本类型和其他语言大同小异。 基本数据类型 整型 整型分为以下两个大类&#xff1a; 按…

echarts 关于折线统计图常用的属性设置--超详细(附加源码)

文章目录 折线统计图设置x轴字体大小及字体颜色设置y轴字体大小及字体颜色设置背景颜色及设置折线颜色设置折线效果图显示阴影折线图位置及标签位置设置鼠标悬浮折线弹出窗口显示对应的数据设置自动横向滚动 总结 大家好&#xff01;近期我会分享几篇关于echarts方面的技术点&a…

easy-es 使用

1、pom中引入依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.14.0</version></dependency><dependency><groupId>org.…

CentOS ens160 显示disconnected

使用nmcli device查看网卡状态&#xff0c;显示如图&#xff1a; 检查宿主机系统VMware DHCP Sevice和VMware NAT Sevice服务是否正常运行。 右键点击我的电脑管理按钮&#xff0c;打开计算机管理点击服务

C语言实例_异或校验算法

一、异或校验算法 异或校验算法&#xff08;XOR校验&#xff09;是一种简单的校验算法&#xff0c;用于检测数据在传输或存储过程中是否发生了错误。通过将数据中的所有比特位相异或&#xff0c;生成一个校验码&#xff0c;然后将该校验码与接收到的数据进行比较&#xff0c;以…

如何大幅提高遥感影像分辨率(Python+MATLAB)

前言: 算法:NSCT算法(非下采样变换) 数据:Landsat8 OLI 遥感图像数据 编程平台:MATLAB+Python 论文参考:毛克.一种快速的全色和多光谱图像融合算法[J].测绘科学,2016,41(01):151-153+98.DOI:10.16251/j.cnki.1009-2307.2016.01.028. 左图:未进行融合的多光谱真彩色合…

ChatGPT逐句逐句地解释代码并分析复杂度的提示词prompt

前提安装chrome 插件 AI Prompt Genius&#xff0c; 请参考 3 个 ChatGPT 插件您需要立即下载 你是首席软件工程师。请解释这段代码&#xff1a;{{code}} 添加注释并重写代码&#xff0c;用注释解释每一行代码的作用。最后分析复杂度。快捷键 / 选择 Explain Code 输入代码提…

内网隧道代理技术(十七)之 NPS的使用

NPS的介绍和使用 NPS介绍 nps是一款轻量级、高性能、功能强大的内网穿透代理服务器。目前支持tcp、udp流量转发,可支持任何tcp、udp上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析等等……),此外还支持内网http代理、内网socks5代理、p2p等,…

RFID技术助力汽车零配件装配产线,提升效率与准确性

随着科技的不断发展&#xff0c;越来越多的自动化设备被应用到汽车零配件装配产线中。其中&#xff0c;射频识别&#xff08;Radio Frequency Identification&#xff0c;简称RFID&#xff09;技术凭借其独特的优势&#xff0c;已经成为了这一领域的重要技术之一。本文将介绍RF…

Cpp基础Ⅰ之编译、链接

1 C是如何工作的 工具&#xff1a;Visual Studio 1.1 预处理语句 在.cpp源文件中&#xff0c;所有#字符开头的语句为预处理语句 例如在下面的 Hello World 程序中 #include<iostream>int main() {std::cout <"Hello World!"<std::endl;std::cin.get…

宝塔部署Java+Vue前后端分离项目经验总结

前言 之前部署服务器都是在Linux环境下自己一点一点安装软件&#xff0c;听说用宝塔傻瓜式部署更快&#xff0c;这次浅浅尝试了一把。 确实简单&#xff01; 1、 买服务器 咋买服务器略&#xff0c;记得服务器装系统就装 Cent OS 7系列即可&#xff0c;我装的7.6。 2、创建…