数据结构-图的最小生成树

news2025/2/3 2:46:17
最小生成树介绍

最小生成树(Minimum Cost Spanning Tree)是代价最小的连通网的生成树,即该生成树上的边的权值和最小

最小生成树的性质:

必须使用且仅使用连通网中的n-1条边来联结网络中的n个顶点;

不能使用产生回路的边;

各边上的权值的总和达到最小

最小生成树在连通网场景中应用广泛,例如

在n个城市之间建立通信网络,如何建立成本最小的网络

在n个城市之间建立公路网络,如何建立成本最小的网络

应用场景:“村村通”,我国系统工程,全国行政村互通:公路、电力、生活和饮用水、电话网、有线电视网、互联网等等

村村通公路,村村通水气电,村村通5G,村村通宽带

2025年全国基本实现村村通

Prim算法

普里姆(Prim)算法设计

假设N=(V,E)是连通网

TE是N上最小生成树中边的集合

  1.U={u0},(u0ÎV), TE={}

  2.在所有uÎU,vÎV-U的边(u,v)ÎE中找一条代价最小的边(u,v0)并入集合TE,同时v0并入U

  3.重复2,直到U=V

算法示例

        普里姆(Prim)算法设计思想:每次找到的最小权值边,一头属于已选顶点U,一头属于未选顶点V-U设置一个辅助数组closedge,用于保存未选顶点的最小权值边closedge,而且这条边的另一个点必然输入已选顶点

推导流程

普里姆(Prim)算法分析

从算法看出,算法只是和顶点数量相关,与边无关

时间复杂度为O(n2)

普里姆适用于边稠密的网,

当一个通信网的点数量少但边数量多,则用普里姆

参考设计代码

class Edge {
public:
    string begin;
    string end;
    int weight;
};
//!!!!!!!!普里姆算法!!!!!!!!!
    void Prim() {
        Edge *q = new Edge[l];
        string s;
        cin >> s;

        int start = find(s);
        for (int i = 0; i < l; i++) {
            arr[i][start] = 0;
        }
        int k = 0;
        int next;
        int remain = l - 1;
        while (remain > 0) {
            arr[start][l] = 1;
            int min = 0xffff;
            for (int i = 0; i < l; i++) {
                if (arr[i][l] == 1) {
                    for (int j = 0; j < l; j++) {
                        if (arr[i][j] < min && arr[i][j] != 0) {
                            min = arr[i][j];
                            start = i;
                            next = j;
                        }
                    }
                }
            }

            q[k].begin = str[start];
            q[k].end = str[next];
            q[k].weight = arr[start][next];
            sum += arr[start][next];
            k++;

            for (int i = 0; i < l; i++) {
                arr[i][next] = 0;
            }
            remain--;
            start = next;
        }


        cout << sum << endl;
        cout << "prim:" << endl;
        for (int i = 0; i < k; i++)
            cout << q[i].begin << ' ' << q[i].end << ' ' << q[i].weight << endl;
    }

 

Kruskal算法

最小生成树生成算法——克鲁斯卡尔(Kruskal)算法

假设G=(V,E)是连通网,设非连通图T={V,{}},图中每个顶点自成一个连通分量

1.在E中找一条代价最小,且其两个顶点分别依附不同的连通分量的边,将其加入T中

2.重复1,直到T中所有顶点都在同一连通分量上

算法示例

算法设计

 克鲁斯卡尔算法设计用选择排序算法,只需要即找出n-1条符合要求的边

难点:如何判断一条边属于两个不同的连通分量

设计辅助数组Belong[n],其中Belong[i]初始为-1,表示顶点i未选

当选中一条边,查看这条边的两个顶点i和j

如果Belong[i]和Belong[j]都是-1,则生成新分量编号并设Belong[i]和Belong[j]

如果Belong[i]或Belong[j]是-1,则生成新分量编号并设Belong[i]或Belong[j]

如果Belong[i]或Belong[j]都不是-1,则说明两个顶点已选

如果Belong[i]==Belong[j],则说明同属一个分量,放弃

如果Belong[i]<>Belong[j],则说明分属不同分量,选择这条边并把两个分量合并,即更改相关分量的Belong值 

 算法分析

算法只是和边数量相关,因此适用于点稠密的网

时间复杂度为O(eloge)

当一个通信网的边数量少但点数量多,则适用

参考代码 

//克鲁斯卡尔算法执行结点
class edge {
public:
    int begin;
    int end;
    int weight;
};
 //!!!!!!!克鲁斯卡尔算法!!!!!
    void Kurskal() {
        for (int i = 0; i < l; i++)
            flag[i] = 0;
        cout << "kruskal:" << endl;
        //将所获得边集数组进行排序
        for (int i = 0; i < t; i++)
            for (int j = 0; j < t - i - 1; j++) {
                if (e[j].weight > e[j + 1].weight)
                    swap(e[j], e[j + 1]);
            }
        for (int i = 0; i < l; i++) {
            int o = find(e[i].begin);
            int p = find(e[i].end);
            if (o != p) {
                flag[o] = p;
                cout << str[e[i].begin] << ' ' << str[e[i].end] << ' ' << e[i].weight << endl;
            }
        }
    }

 

完整代码(仅供参考)
#include<iostream>
#include<queue>

#define nu 0xffff;
using namespace std;

class node {
public:
    int info;
    node *next;

    node() {
        next = nullptr;
    }
};

class point {
public:
    int data;
    node *head;

    point() {
        head = nullptr;
    }
};

//克鲁斯卡尔算法执行结点
class edge {
public:
    int begin;
    int end;
    int weight;
};

class Edge {
public:
    string begin;
    string end;
    int weight;
};

class graph {
private:
    int l, t;
    int **arr;
    string *str;
    int *flag;
    edge *e;
    int sum = 0;
    point *a;
    int n, m;
public:
    graph() {
        Create();
    }
    //邻接矩阵存储
    void Create() {
        cin >> l;
        str = new string[l];
        flag = new int[l];

        arr = new int *[l + 1];
        for (int i = 0; i < l; i++) {
            flag[i] = 0;
            arr[i] = new int[l];
        }
        for (int i = 0; i < l; i++)
            for (int j = 0; j < l; j++)
                arr[i][j] = nu;
        for (int i = 0; i < l; i++)
            cin >> str[i];

        cin >> t;
        for (int i = 0; i < t; i++) {
            string o, p;
            int num;
            cin >> o >> p >> num;
            int oo = find(o), pp = find(p);
            arr[oo][pp] = arr[pp][oo] = num;
            e[i].begin = oo;
            e[i].end = pp;
            e[i].weight = num;
        }


        for (int i = 0; i < l; i++) {
            for (int j = 0; j < l; j++)
                cout << arr[i][j] << ' ';
            cout << endl;
        }
    }


    //克鲁斯卡尔算法
    int find(int i) {
        while (flag[i] > 0)
            i = flag[i];
        return i;
    }

    int find(const string &ss) {
        for (int i = 0; i < l; i++)
            if (str[i] == ss)
                return i;
        return -1;
    }

    //!!!!!!!克鲁斯卡尔算法!!!!!
    void Kurskal() {
        for (int i = 0; i < l; i++)
            flag[i] = 0;
        cout << "kruskal:" << endl;
        //将所获得边集数组进行排序
        for (int i = 0; i < t; i++)
            for (int j = 0; j < t - i - 1; j++) {
                if (e[j].weight > e[j + 1].weight)
                    swap(e[j], e[j + 1]);
            }
        for (int i = 0; i < l; i++) {
            int o = find(e[i].begin);
            int p = find(e[i].end);
            if (o != p) {
                flag[o] = p;
                cout << str[e[i].begin] << ' ' << str[e[i].end] << ' ' << e[i].weight << endl;
            }
        }
    }


    //!!!!!!!!普里姆算法!!!!!!!!!
    void Prim() {
        Edge *q = new Edge[l];
        string s;
        cin >> s;

        int start = find(s);
        for (int i = 0; i < l; i++) {
            arr[i][start] = 0;
        }
        int k = 0;
        int next;
        int remain = l - 1;
        while (remain > 0) {
            arr[start][l] = 1;
            int min = 0xffff;
            for (int i = 0; i < l; i++) {
                if (arr[i][l] == 1) {
                    for (int j = 0; j < l; j++) {
                        if (arr[i][j] < min && arr[i][j] != 0) {
                            min = arr[i][j];
                            start = i;
                            next = j;
                        }
                    }
                }
            }

            q[k].begin = str[start];
            q[k].end = str[next];
            q[k].weight = arr[start][next];
            sum += arr[start][next];
            k++;

            for (int i = 0; i < l; i++) {
                arr[i][next] = 0;
            }
            remain--;
            start = next;
        }


        cout << sum << endl;
        cout << "prim:" << endl;
        for (int i = 0; i < k; i++)
            cout << q[i].begin << ' ' << q[i].end << ' ' << q[i].weight << endl;
    }

};

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

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

相关文章

linux vim 异常退出 异常处理 交换文件

交换文件 *.swp 格式 同时是隐藏的 如在vim一个文件&#xff0c; 在没有正常退出&#xff0c; 如直接断开连接 在次编辑这个文件 会出现下图的错误 解决方案&#xff1a; 直接删除这个交换文件即可 rm -fr .zen.txt.swp

css3 属性 backface-visibility 的实践应用

backface-visibility 是一个用于控制元素在面对屏幕不同方向时的可见性的CSS3特性。它有两个可能的值&#xff1a; visible&#xff1a;当元素不面向屏幕&#xff08;即背面朝向用户&#xff09;时&#xff0c;元素的内容是可以被看到的。hidden&#xff1a;当元素不面向屏幕…

【计算机网络】Socket的SO_REUSEADDR选项与TIME_WAIT

SO_REUSEADDR用于设置套接字的地址重用。当一个套接字关闭后&#xff0c;它的端口可能会在一段时间内处于TIME_WAIT状态&#xff0c;此时无法立即再次绑定相同的地址和端口。使用SO_REUSEADDR选项可以允许新的套接字立即绑定到相同的地址和端口&#xff0c;即使之前的套接字仍处…

如何使用Linux Archcraft结合内网穿透实现SSH远程连接

&#x1f4d1;前言 本文主要是使用Linux Archcraft结合内网穿透实现SSH远程连接的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#…

过年了,程序员们,请多关照自己!休息是为了走得更远!

文章目录 过年了&#xff0c;程序员们&#xff0c;请多关照自己&#xff01;一、理解“卷”背后的代价二、休息是为了走得更远三、关注健康&#xff0c;远离“过劳”四、平衡工作与生活&#xff0c;追求全面发展 过年了&#xff0c;程序员们&#xff0c;请多关照自己&#xff0…

input框前面名字长短不一时,让上下input框对齐方法

没设置之前 设置之后&#xff1a; 代码如下&#xff1a; <style>div{width: 500px;}label {display: block; /* 设置 label 元素为块级元素 */text-align: right; /* 设置文本右对齐 */margin-bottom: 10px; /* 设置标签之间的间距 */} </style> </head><…

Aigtek射频功率放大器有哪些具体应用

射频功率放大器是一种用于增加射频信号功率的电子器件。它在众多领域中有着广泛的具体应用&#xff0c;下面安泰电子将详细介绍几个主要的应用领域。 无线通信&#xff1a;射频功率放大器在无线通信系统中扮演着重要的角色。在移动通信领域&#xff0c;如蜂窝网络和卫星通信系统…

Solidworks 与 MATLAB 联合仿真

本文主要讲解了“MATLAB与SolidWorks的联合仿真怎么实现”&#xff0c;文中的讲解内容简单清晰&#xff0c;易于学习与理解&#xff0c;下面请大家跟着小编的思路慢慢深入&#xff0c;一起来研究和学习“MATLAB与SolidWorks的联合仿真怎么实现”吧&#xff01; 下载插件。 1、…

linux免密登录的实现

ssh免密登录使用方便&#xff0c;关键没有了口令验证反倒规避了暴力破解或者被探测的风险。配置得当&#xff0c;使用ssh免密登录更加安全。在生产环境中应用和数据库服务器之间互相设置后使用方便&#xff0c;并且在第三方人员配置使用时不用告知对方密码。 第一步、ssh登录发…

Python itertools模块:生成迭代器(实例分析)

itertools 模块中主要包含了一些用于生成迭代器的函数。在 Python 的交互式解释器中先导入 itertools 模块&#xff0c;然后输入 [e for e in dir(itertools) if not e.startswith(_)] 命令&#xff0c;即可看到该模块所包含的全部属性和函数&#xff1a; >>> [e for …

RabbitMQ控制台的基本使用

启动RabbitMQ后&#xff0c;浏览器 http://localhost:15672 打开RabbitMQ的控制台页面后&#xff0c;登录默认账户guest。 一. 添加队列 控制台选择队列&#xff0c;然后选择添加队列&#xff0c;队列类型默认经典类型&#xff0c;然后输入队列名称&#xff0c;最后添加队列。…

JSP和JSTL板块:第三节 JSP四大域对象 来自【汤米尼克的JAVAEE全套教程专栏】

JSP和JSTL板块&#xff1a;第三节 JSP四大域对象 一、page范围二、request范围三、session范围四、application范围 在服务器和客户端之间、各个网页之间、哪怕同一个网页之内&#xff0c;总是需要传递各种参数值&#xff0c;这时JSP的内置对象就是传递这些参数的载具。内置对象…

JeecgBoot jmreport/loadTableData RCE漏洞复现(CVE-2023-41544)

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

虫情监测设备能够自动识别病虫害

TH-CQ3S虫情监测设备的工作原理主要是通过高清摄像头拍摄农田的实时图像&#xff0c;利用图像识别技术对图像中的病虫害进行自动识别。一旦发现病虫害&#xff0c;设备会自动发出警报&#xff0c;并通过手机APP通知农民。农民可以根据设备提供的预测预报&#xff0c;及时采取防…

API攻击是什么?如何做好防范

API 攻击是针对应用程序接口的一种攻击手段&#xff0c;近年来逐渐成为网络安全领域的热点话题。攻击者主要针对应用程序接口中的漏洞或者错误进行API攻击&#xff0c;从而达到窃取敏感数据、进行恶意操作、破坏系统正常运行等恶意目的。 什么是API攻击&#xff1f; API 攻击是…

window 安装 jenkins 编写脚本

set JAVA_HOMED:\RuanJianKaiFa\jdk\jdk11 set CLASSPATH.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOMe%\lib\tools.jar; set Path%JAVA_HOME%\bin; java -jar jenkins.war 下载jenkins.war包&#xff0c;编写一个txt文档&#xff0c;把脚本复制进去&#xff0c;修改文件后缀为.bat文件…

【MySQL】MySQL库

使用C/C语言链接MySQL 一、mysql connect二、mysql 接口介绍1. 初始化 mysql_init()2. 链接数据库 mysql_real_connect()3. 执行 mysql 命令 mysql_query()4. 获取执行结果 mysql_store_result()5. 释放空间5. 关闭 mysql 链接 mysql_close() 一、mysql connect 要使用C语言连…

【JavaEE进阶】 图书管理系统开发日记——贰

文章目录 &#x1f332;前言&#x1f384;设计数据库&#x1f343;引⼊MyBatis和MySQL驱动依赖&#x1f333;Model创建&#x1f38d;约定前后端交互接口&#x1f340;服务器代码&#x1f6a9;控制层&#x1f6a9;业务层&#x1f6a9;数据层 &#x1f334;效果展示⭕总结 &#…

亚马逊速卖通shein测评补单,轻松获得高评店铺订单暴涨人气火爆

测评之所以被认为是最快速有效的推广方式&#xff0c;是因为它能够迅速影响多个关键因素。通过测评&#xff0c;您能够快速提升关键词的转化率&#xff0c;从而获得更好的搜索排名。优质的评价有助于增加产品的权重和转化率&#xff0c;进一步提升排名。同时&#xff0c;增加的…

whistle抓包时如何过滤掉无用的链接

whistle在抓包的时候经常会有一些图片等我们不需要的地址&#xff0c;过多时会影响到我们抓的接口的速度&#xff0c;如何能快速的过滤掉无用的链接呢 可以在setting下进行如下设置 Exclude Filter&#xff1a;排除筛选器&#xff0c;即勾选后会排除勾选框下的地址链接Includ…