[C++][数据结构][图][中][图的遍历][最小生成树]详细讲解

目录

  • 1.图的遍历
    • 1.广度优先遍历
    • 2.深度优先遍历
  • 2.最小生成树
    • 1.Kruskal算法
    • 2.Prim算法


1.图的遍历

  • 给定一个图G和其中任意一个顶点 v 0 v_0 v0,从 v 0 v_0 v0出发,沿着图中各边访问图中的所有顶点,且每个顶 点仅被遍历一次
    • “遍历”:对结点进行某种操作的意思

1.广度优先遍历

请添加图片描述

  • **例如:**现在要找东西,假设有三个抽屉,东西在哪个抽屉不清楚,现在要将其找到,广度优先遍历的做法是:

    1. 先将三个抽屉打开,在最外层找一遍
    2. 将每个抽屉中红色的盒子打开,再找一遍
    3. 将红色盒子中绿色盒子打开,再找一遍
    4. 直到找完所有的盒子
      • 注意:每个盒子只能找一次,不能重复找
        请添加图片描述
  • 思考:如何防止节点被重复遍历?

    • 增加一个数组,用于标记是否入过队列,这样可以防止重复遍历
void BFS(const V& src)
{size_t srci = GetVertexIndex(src);queue<int> q;vector<bool> visited(_vertexs.size(), false); // 标记数组q.push(srci);visited[srci] = true;int levelSize = 1; // 控制每层出的数量while (!q.empty()){// 一层一层出for (size_t i = 0; i < levelSize; i++){int front = q.front();q.pop();cout << front << ":" << _vertexs[front] << " ";// 把front的邻接顶点入队列for (size_t j = 0; j < _vertexs.size(); j++){if (_matrix[front][j] != MAX_W && visited[j] == false){q.push(j);visited[j] = true;}}}cout << endl;levelSize = q.size();}
}

2.深度优先遍历

请添加图片描述

  • **例如:**现在要找东西,假设有三个抽屉,东西在哪个抽屉不清楚,现在要将其找到,深度优先遍历的做法是:
    1. 先将第一个抽屉打开,在最外层找一遍
    2. 将第一个抽屉中红盒子打开,在红盒子中找一遍
    3. 将红盒子中绿盒子打开,在绿盒子中找一遍
    4. 递归查找剩余的两个盒子
    • **深度优先遍历:**将一个抽屉一次性遍历完(包括该抽屉中包含的小盒子),再去递归遍历其他盒子
  • 如果给的图不是连通图,以某个顶点为起点没有遍历完成,怎么保证遍历完剩下的顶点
    • 在visited数组中找没有遍历过的顶点,再次进行遍历
void _DFS(size_t srci, vector<bool>& visited)
{cout << srci << ":" << _vertexs[srci] << endl;visited[srci] = true;for (size_t i = 0; i < _vertexs.size(); i++){if (_matrix[i] != MAX_W && visited[i] == false){_DFS(i, visited);}}
}void DFS(const V& src)
{size_t srci = GetVertexIndex(src);vector<bool> visited(_vertexs.size(), false);_DFS(srci, visited);// 处理存在不连通的情况for (size_t i = 0; i < _vertexs.size(); i++){if (!visited[i]){_DFS(i, visited);}}
}

2.最小生成树

  • 连通图中的每一棵生成树,都是原图的一个极大无环子图,即:
    • 从其中删去任何一条边,生成树就不在连通
    • 反之,在其中引入任何一条新边,都会形成一条回路
  • 若连通图由n个顶点组成,则其生成树必含n个顶点和n-1条边,因此构造最小生成树的准则有三条:
    • 只能使用图中权值最小的边来构造最小生成树
      • 最小的成本让着N个顶点连通
    • 只能使用恰好n-1条边来连接图中的n个顶点
    • 选用的n-1条边不能构成回路
  • 构造最小生成树的方法:Kruskal算法和Prim算法,这两个算法都采用了逐步求解的贪心策略
  • 贪心算法:
    • 指在问题求解时,总是做出当前看起来最好的选择
      • 即:贪心算法做出的不是整体最优的的选择,而是某种意义上的局部最优解
    • 贪心算法不是对所有的问题都能得到整体最优解

1.Kruskal算法

  • 任给一个有n个顶点的连通网络 N = { V , E } N=\{V,E\} N={V,E}
    • 首先构造一个由这n个顶点组成、不含任何边的图 G = { V , N U L L } G=\{V,NULL\} G={V,NULL},其中每个顶点自成一个连通分量
    • 其次不断从E中取出权值最小的一条边(若有多条任取其一),若该边的两个顶点来自不同的连通分量,则将此边加入到G中
      • 如此重复,直到所有顶点在同一个连通分量上为止
    • 核心每次迭代时,选出一条具有最小权值,且两端点不在同一连通分量上的边,加入生成树
      • Kruskal算法是一种全局贪心的算法
  • 如何判断是否形成环?
    • 并查集
  • 在下图执行Kruskal算法的过程
    • 加了阴影的边属于不断增长的森林A
    • 该算法按照边的权重大小依次进行考虑,箭头指向的边是算法每一步考察的边
      • 如果该条边将两颗不同的树连接起来,它就被加入到森林里,从而完成对两棵树的合并
        请添加图片描述
W Kruskal(Self& minTree)
{size_t n = _vertexs.size();// 初始化minTreeminTree._vertexs = _vertexs;minTree._indexMap = _indexMap;minTree._matrix.resize(n);for (size_t i = 0; i < n; i++){minTree._matrix[i].resize(n, MAX_W);}priority_queue<Edge, vector<Edge>, greater<Edge>> minQueue;// 建堆排序for (size_t i = 0; i < n; i++){for (size_t j = 0; j < n; j++){if (i < j && _matrix[i][j] != MAX_W){minQueue.push(Edge(i, j, _matrix[i][j]));}}}// 选出n-1条边size_t size = 0;W totalW = W();UnionFindSet ufs(n);while (!minQueue.empty()){Edge min = minQueue.top();minQueue.pop();// 判环 -> 并查集if (!ufs.InSameSet(min._srci, min._dsti)){cout << _vertexs[min._srci] << "->" \<< _vertexs[min._dsti] << ":" << min._w << endl;minTree._AddEdge(min._srci, min._dsti, min._w);ufs.Union(min._srci, min._dsti); // 入集size++;totalW += min._w;}else{cout << "Forming Ring: ";cout << _vertexs[min._srci] << "->" \<< _vertexs[min._dsti] << ":" << min._w << endl;}}if (size == n - 1){return totalW;}else{return W();}
}

2.Prim算法

  • Prim算法的一个性质集合A中的边总是构成一棵树,这棵树从一个任意的根节点r开始,一直长大到覆盖V中的所有结点时为止
    • Prim算法思路天然避环
    • 算法每一步在连续集合A和A之外的结点的所有边中,选择一条轻量级边加入到A中
    • 本策略也属于贪心策略,因为每一步所加入的边都必须是使树的总权重增加量最小的边
      • Prim算法是一种局部贪心算法
  • 在下图执行Prim算法的过程
    • 初始的根节点为a,加阴影的边和黑色的结点都属于树A
    • 在算法的每一步,树中的结点就决定了图的一个切割,横跨该切割的一条轻量级边被加入到树中
    • **例如:**在途中第二步,该算法可以选择将边 ( b , c ) (b, c) (b,c)加入到树中,也可以将边 ( a , h ) (a, h) (a,h)加入到树中,因为这两条边都是横跨该切割的轻量级边
      请添加图片描述
W Prim(Self& minTree, const W& src)
{size_t srci = GetVertexIndex(src);size_t n = _vertexs.size();// 初始化minTreeminTree._vertexs = _vertexs;minTree._indexMap = _indexMap;minTree._matrix.resize(n);for (size_t i = 0; i < n; i++){minTree._matrix[i].resize(n, MAX_W);}// true & false表示该元素是否在该集合内vector<bool> X(n, false);vector<bool> Y(n, true);X[srci] = true;Y[srci] = false;// 从X->Y集合中连接的边里面选出最小的边priority_queue<Edge, vector<Edge>, greater<Edge>> minQueue;// 先把srci连接的边添加到队列中for (size_t i = 0; i < n; i++){if (_matrix[srci][i] != MAX_W){minQueue.push(Edge(srci, i, _matrix[srci][i]));}}size_t size = 0;W totalW = W();while (!minQueue.empty()){Edge min = minQueue.top();minQueue.pop();// 最小边的目标也在X集合,则构成环if (X[min._dsti]){cout << "Forming Ring:";cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;}else{cout << _vertexs[min._srci] << "->" << _vertexs[min._dsti] << ":" << min._w << endl;minTree._AddEdge(min._srci, min._dsti, min._w);X[min._dsti] = true;Y[min._dsti] = false;size++;totalW += min._w;// 可能最小生成树已经生成,但是多了很多成环边,无须继续遍历if (size == n - 1){break;}// 将目标顶点连接的边加入到队列中for (size_t i = 0; i < n; i++){if (_matrix[min._dsti][i] != MAX_W && Y[i]){minQueue.push(Edge(min._dsti, i, _matrix[min._dsti][i]));}}}}// 实际不一定存在最小生成树if (size == n - 1){return totalW;}else{return W();}
}

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

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

相关文章

《Windows API每日一练》5.1 键盘基础

本节我们讲述关于键盘的一些基础知识。当我们按下一个键盘按键时&#xff0c;会产生一个键盘按键消息。这一点你能确定吗&#xff1f;假如是一个菜单快捷键消息&#xff0c;或者是一个子窗口控件消息呢&#xff1f;这就超出了本节讨论的范围&#xff0c;我们将在菜单和子窗口控…

Jenkins+K8s实现持续集成(二)

部署前呢&#xff0c;要先把jenkins搭建好。 同时呢已经有了k8s的环境。 基于以上两步已经有了的情况&#xff0c;继续要实现jenkinsk8s持续集成&#xff0c;需要先准备四个文件&#xff1a; Dockerfile首先要准备好一个Dockerfile文件&#xff0c;用于构建Docker镜像的文本…

安装cuda、cudnn、Pytorch(用cuda和cudnn加速计算)

写在前面 最近几个月都在忙着毕业的事&#xff0c;好一阵子没写代码了。今天准备跑个demo&#xff0c;发现报错 AssertionError: Torch not compiled with CUDA enabled 不知道啥情况&#xff0c;因为之前有cuda环境&#xff0c;能用gpu加速&#xff0c;看这个报错信息应该是P…

React常用方法汇总【更新中】

文章目录 前言创建项目启动命令列表渲染父子组件传值useEffect 异步函数使用方法useEffect 异步函数清除方法控制组件显示隐藏axios 安装使用 前言 运行 react 需要先安装 node.js&#xff0c;具体安装步骤可以参考这篇文章 https://blog.csdn.net/weixin_43721000/article/de…

如何在 MySQL 或 MariaDB 中导入和导出数据库

作者选择了自由开源基金会作为 Write for Donations 计划的捐赠对象。 介绍 在软件开发中&#xff0c;导入和导出数据库是一项常见任务。您可以使用数据转储来备份和恢复信息。您还可以使用它们将数据迁移到新的服务器或开发环境。 在本教程中&#xff0c;您将使用 MySQL 或…

spring xml实现bean的生命周期

spring xml实现bean的生命周期 搭建maven工程&#xff0c;导入maven依赖(需要jdk17或者降低spring-context版本) <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><versio…

Linux GCC G++:各个版本发行年份及新增特性盘点

目录 GCC盘点 GCC 4.0 GCC 4.1 GCC 4.2 GCC 4.3 GCC 4.4 GCC 4.5 GCC 4.6 GCC 4.7 GCC 4.8 GCC 4.9 GCC 5.0 GCC 6.0 GCC 7.0 GCC 8.0 GCC 9.0 GCC 10.0 GCC 11.0 GCC 12.0 GCC 13.0 GCC稳定版本 GCC 4.8 GCC 4.9 GCC 5.0 GCC 6.0 GCC 7.0 GCC 8.0 …

Vue65-vue-resource:ajax请求

vue-resource是vue的插件库&#xff0c;用vue.use(xxxx)使用插件。 1、安装 2、引入和使用 这个库&#xff0c;维护的频率不高了。还是建议使用&#xff1a;axios&#xff0c;vue-resource只是了解即可。

MySQL8,Navicat能登陆成功,密码却忘记了

执行成功的图&#xff1a; 以下为步骤&#xff1a;本文一共8个简单步骤。 环境&#xff1a;mysql8、window10、navicat11 1、打开本地电脑window10的命令窗&#xff08;俗称黑窗口&#xff09;&#xff0c;windowR 2、输入regegit&#xff0c;回车&#xff0c;打开注册表 3、…

【React】《React 学习手册 (第2版) 》笔记-Chapter12-React 服务器端渲染

十二、React 服务器端渲染 我们可以采用同构方式渲染 React&#xff0c;以便支持浏览器以外的平台。这意味着&#xff0c;我们可以在服务器端渲染 UI&#xff0c;然后再发给浏览器。借助服务器端渲染&#xff0c;可以提升性能、增进可移植性、提高安全性。 同构&#xff08;is…

Flink Time 详解

Flink在处理实时数据流时&#xff0c;时间是一个核心概念。Flink支持多种时间语义&#xff0c;以满足不同业务场景的需求。以下是对Flink中时间概念的详细解释&#xff1a; 一、时间概念概述 在Flink中&#xff0c;时间主要分为三种类型&#xff1a; 事件时间&#xff08;Eve…

技术差异,应用场景;虚拟机可以当作云服务器吗

虚拟机和云服务器是现在市面上常见的两种计算资源提供方式&#xff0c;很多人把这两者看成可以相互转换或者替代的物品&#xff0c;实则不然&#xff0c;这两种资源提供方式有许多相似之处&#xff0c;但是也有不少区别&#xff0c;一篇文章教你识别两者的技术差异&#xff0c;…

【全文档】软件项目经理需要掌握的文档有哪些?

软件项目经理在项目管理过程中需要编写多种文档&#xff0c;以下是常见的十五个文档&#xff1a; 项目计划&#xff1a; 详细描述了项目的范围、时间、成本、资源、沟通计划等关键信息&#xff0c;是项目管理的核心文档。 需求文档&#xff1a; 记录了项目的业务需求、功能需求…

vue3第二阶段的开发文档

1 2.1 案例——学习计划表 2.1.1 准备工作 在开发“学习计划表”案例之前&#xff0c;需要先完成一些准备工作&#xff0c;具体步骤如下。 ① 打开命令提示符&#xff0c;切换到 D:\vue\chapter02 目录&#xff0c;在该目录下执行如下命令&#xff0c;创建 项目。 np…

深入理解预处理

1.预定义符号 C语言设置了⼀些预定义符号&#xff0c;可以直接使用&#xff0c;预定义符号也是在预处理期间处理的。 __FILE__ //进⾏编译的源⽂件 __LINE__ //⽂件当前的⾏号 __DATE__ //⽂件被编译的⽇期 __TIME__ //⽂件被编译的时间 __STDC__ //如果编译器遵循ANSI C&…

3. ceph-mimic版本部署

ceph-mimic版本部署 一、ceph-mimic版本部署1、环境规划2、系统基础环境准备2.1 关闭防火墙、SELinux2.2 确保所有主机时间同步2.3 所有主机ssh免密2.4 添加所有主机解析 3、配置ceph软件仓库4、安装ceph-deploy工具5、ceph集群初始化6、所有ceph集群节点安装相关软件7、客户端…

AI-“国外一开源,国内就创新”!

大模型领域中美差距有多大&#xff1f; 近年来&#xff0c;人工智能技术在全球范围内迅速发展&#xff0c;特别是在大型语言模型和机器学习方法方面取得了突破性进展。在这个领域&#xff0c;计算能力的提升也推动了技术的革命性进步。 作为世界上两大的重要经济体&#xff0…

Java程序之简单求和

题目&#xff1a; 求saaaaaaaaaaaa...a的值&#xff0c;其中a是一个数字。要求输入a的值和加数的个数n&#xff0c;例如&#xff1a;a2&#xff0c;n5&#xff0c;此时s222222222222222&#xff0c;输出s的值。 源代码&#xff1a; package Question5;import java.util.Scann…

LeetCode322.零钱兑换(一)

LeetCode刷题记录 文章目录 &#x1f4dc;题目描述&#x1f4a1;解题思路⌨C代码 &#x1f4dc;题目描述 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。…

Macbook M1 Fusion安装Debian/Linux

背景 本人主力工作电脑已经迁移到苹果芯片m1的macbook上&#xff0c;曾经尝试使用Fusion安装CentOS、OpenEuler、Ubuntu的一些版本&#xff0c;都没有安装成功。最近开始研究Linux/Unix系统编程&#xff0c;迫切需要通过VMware Fusion安装一台Linux操作系统的虚拟机。 Linux安…