探索数据结构:图(二)之图的遍历,Kruskal与Prim算法


✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法
贝蒂的主页:Betty’s blog

1. 图的遍历

图的遍历方式一般分为两种:深度优先遍历广度优先遍历

1.1 深度优先遍历

1.1.1 算法思想

**深度优先遍历(Depth First Search,DFS)**是一种用于遍历或搜索图的算法,过程类似于树的前序遍历。其基本原理为:从图中的某个顶点出发,沿着一条路径尽可能深地探索,直到无法继续前进时,回溯到上一个顶点,再尝试其他未探索的路径。这个过程不断重复,直到所有顶点都被访问到。

具体步骤为:

  1. 首先选择一个起始顶点,并将其标记为已访问。
  2. 从起始顶点出发,选择一个未访问过的邻接顶点,继续进行深度优先遍历。
  3. 如果当前顶点没有未访问的邻接顶点,则回溯到上一个顶点,继续探索上一个顶点的其他未访问邻接顶点。
  4. 重复步骤 2 和 3,直到所有顶点都被访问。

比如我们对下面这幅图进行深度优先遍历:

我们首先从A点开始搜索,沿A,B,E,G一直搜索到G然后回溯到B,接着从B开始沿着C,F,D搜索到D,回溯到F,最后沿着H,I搜索到I,搜索完毕。

1.1.2 具体实现

首先我们通过映射关系找到对应下标,然后定义一个判断该点是否已访问的bool数组,最后进行递归访问。在递归访问中,我们首先访问该数据,然后判断该顶点连接的边是否已访问,如果未访问则递归访问,直到所有边都被访问为止。

void _dfs(int srci, vector<bool>& visited)
{//先访问cout << _vertexs[srci] << " ";//标记已访问visited[srci] = true;for (int i = 0; i < _vertexs.size(); i++){//如果存在边且未被遍历if (_matrix[srci][i] != MAX_W && visited[i] == false){_dfs(i, visited);}}
}
//深度优先遍历
void dfs(const V& src)
{int srci = getVertexsIndex(src);//标记已访问的数组vector<bool> visited(_vertexs.size(), false);_dfs(srci, visited);cout << endl;
}

1.2 广度优先遍历

1.2.1 算法思想

**广度优先遍历(Breadth-First Search,BFS)**是一种用于遍历或搜索图的算法,过程类似于树的层序遍历。其基本原理为:从图中的某个顶点出发,先访问该顶点的所有邻接顶点,再依次访问这些邻接顶点的邻接顶点,如此逐步向外扩展,直到所有顶点都被访问到。

具体步骤为:

  1. 首先选择一个起始顶点,并将其标记为已访问。
  2. 创建一个队列,将起始顶点放入队列中。
  3. 从队列中取出一个顶点,访问该顶点,并将其所有未访问过的邻接顶点放入队列中。
  4. 重复步骤 3,直到队列为空。

比如我们对下面这幅图进行广度优先遍历:

我们首先从A点开始搜索,然后将B,C,D添加到队列,搜索B,C,D过程中再将E,F添加到队列中,接着搜索E,F过程中再将H,G添加到队列,最后搜索完H,G,将I添加到队列,然后搜索完I即搜索完毕。

1.2.2 具体实现

首先我们通过映射关系找到对应下标,然后定义一个判断该点是否已访问的bool数组,将起始顶点对应的下标加入队列。然后访问队列中的元素,访问完的数据我们需要先出队列。最后判断该顶点连接的边是否已访问,如果未访问则继续加入队列,直到所有边都被访问完为止。

//广度优先遍历
void bfs(const V& src)
{//获取对应下标int srci = getVertexsIndex(src);queue<int> q;//标记是否被访问的数组vector<bool> visited(_vertexs.size(), false);q.push(srci);visited[srci] = true;while (!q.empty()){int front = q.front();q.pop();cout << _vertexs[front] << " ";for (int i = 0; i < _vertexs.size(); i++){//如果存在边,并且没有被访问if (_matrix[front][i] != MAX_W && visited[i] == false){q.push(i);visited[i] = true;}}}cout << endl;
}

2. 最小生成树算法

首先我们回忆一下,一个连通图的最小连通子图称为该图的生成树,有 n n n个顶点的连通图的生成树有 n n n个顶点和 n − 1 n - 1 n1条边。最小生成树指的是一个图的生成树中,总权值最小的生成树,并且权值都为正数。

常用的最小生成树算法有两种:Kruskal算法(克鲁斯卡尔算法)Prim算法(普里姆算法),这两者都采用了贪心的策略。

2.1 Kruskal

2.1.1 算法思想

Kruskal算法是一种用于求解无向加权图最小生成树的算法。该算法的目标是在图中找到一个连通子图,它包含了图中的所有顶点,并且边的权值之和最小。其基本思想是按照边的权值从小到大的顺序依次选择边,在选择边的过程中,避免形成回路。
具体步骤如下:

  1. 首先将图中的所有边按照权值从小到大进行排序。
  2. 从权值最小的边开始,依次检查每条边。
    • 如果选择这条边不会形成回路,则将其加入最小生成树中。
    • 如果选择这条边会形成回路,则跳过该边,继续检查下一条边。
  3. 重复步骤 2,直到最小生成树中包含了图中的所有顶点,或者已经检查完了所有的边。
  1. 首先第一步对所有边的权值进行一次排序,然后选出权值最小的边h-g

  1. 继续选择权值最小的边,分别选择了g-fc-ia-bc-f。然后选择i-g时,形成回路,跳过此边。

  1. 继续选择权值最小的边,选择c-d,再选择h-i形成回路。然后跳过,继续选择a-hb-c形成回路。

  1. 最后选择边d-e,接下来无论选择任何边都会形成回路,并且此时边数恰好比节点数少一,选择完毕。

2.1.2 具体实现

每次选取权值最小的边,我们可以使用优先级队列priority_queue实现。判断是否成环,我们可以利用前面我们实现的并查集UnionFindSet实现。并且值得注意的是:向堆中添加边时,无向图只需要添加一次即可。

struct Edge
{int _srci; //源顶点的下标int _desti; //目标顶点的下标W _weight; //权值Edge(int srci, int desti, const W& weight):_srci(srci), _desti(desti), _weight(weight){}bool operator > (const Edge& edge) const{return _weight > edge._weight;}
};
//Kruskal算法
W Kruskal(Graph<V, W, MAX_W, Direction>& minTree)
{//初始化int n = _vertexs.size();minTree._vertexs = _vertexs;minTree._indexMap = _indexMap;minTree._matrix.resize(n, vector<W>(n, MAX_W));//小堆priority_queue<Edge, vector<Edge>, greater<Edge>> minHeap;for (int i = 0; i < n; i++){//无向图只需添加一半边for (int j = 0; j < i; j++){if (_matrix[i][j] != MAX_W){minHeap.push(Edge(i, j, _matrix[i][j]));}}}UnionFindSet ufs(n);//并查集判断是否成环int count = 0;//已选边的数量W total = W();//计算选出总权值while (!minHeap.empty() && count < n - 1){//选最小边Edge minEdge = minHeap.top();minHeap.pop();int srci = minEdge._srci, desti = minEdge._desti;W weight = minEdge._weight;if (!ufs.inSameSet(srci, desti)){//添加边minTree._matrix[srci][desti] = weight;if (Direction == false){minTree._matrix[desti][srci] = weight;}ufs.unionSet(srci, desti);//边数++count++;total += weight;cout << "选边: " << _vertexs[srci] << "->" << _vertexs[desti] << ":" << weight << endl;}else{cout << "成环: " << _vertexs[srci] << "->" << _vertexs[desti] << ":" << weight << endl;}}//边数与比节点少一if (count == n - 1){cout << "构成最小生成树" << endl;return total;}else{cout << "无法构成最小生成树" << endl;return W();}
}

2.2 Prim

2.2.1 算法思想

Prim算法是一种用于求解无向加权图最小生成树的算法。该算法的目标同样是在图中找到一个连通子图,它包含了图中的所有顶点,并且边的权值之和最小。其基本思想是以一个顶点为起点,逐步向外扩展,每次选择一条连接已加入最小生成树的顶点集和未加入顶点集的权值最小的边。
具体步骤如下:

  1. 任选一个顶点作为起始顶点,将其加入最小生成树中。
  2. 维护一个集合,记录已加入最小生成树的顶点。初始时,该集合只包含起始顶点。
  3. 对于已加入最小生成树的每个顶点,检查其所有邻接边,找到连接已加入顶点集和未加入顶点集的权值最小的边。
  4. 将找到的权值最小的边对应的顶点加入最小生成树中,并更新已加入顶点集。
  5. 重复步骤 3 和 4,直到最小生成树中包含了图中的所有顶点。
  1. 首先从a点开始选择,其中与a点直接相连的边有a-ba-h,选择权值最小的边a-b加入。

  1. 同样以a,b作为起始点选取权值最小的边b-c,同理选出c-i

  1. 同样以a,b,c,i作为起始点选取权值最小的边c-f,同理选出f-g

  1. 最后再依次g-hc-dd-e,选取完毕。

2.2.2 具体实现

为了防止成环,我们采用定义一个bool类似数组

//Prim算法
W Prim(Graph<V, W, MAX_W, Direction>& minTree,const V& start)
{//初始化int n = _vertexs.size();minTree._vertexs = _vertexs;minTree._indexMap = _indexMap;minTree._matrix.resize(n, vector<W>(n, MAX_W));int starti = getVertexsIndex(start);//获取对应下标//小堆priority_queue<Edge, vector<Edge>, greater<Edge>> minHeap;//已访问顶点的集合vector<bool> visited(n, false);visited[starti] = true;for (int i = 0; i < n; i++){//将该顶点相连的边全部添加进堆if (_matrix[starti][i] != MAX_W){minHeap.push(Edge(starti, i, _matrix[starti][i]));}}int count = 0;//已选边的数量W total = W();//计算选出总权值while (!minHeap.empty() && count < n - 1){//选最小边Edge minEdge = minHeap.top();minHeap.pop();int srci = minEdge._srci, desti = minEdge._desti;W weight = minEdge._weight;//如果该节点没有被添加进集合中if (visited[desti] == false){for (int i = 0; i < n; i++){//将该顶点相连的边全部添加进堆if (_matrix[desti][i] != MAX_W && visited[i] == false){minHeap.push(Edge(desti, i, _matrix[desti][i]));}}//添加边minTree._matrix[srci][desti] = weight;if (Direction == false){minTree._matrix[desti][srci] = weight;}visited[desti] = true;//边数++count++;total += weight;cout << "选边: " << _vertexs[srci] << "->" << _vertexs[desti] << ":" << weight << endl;}else{cout << "成环: " << _vertexs[srci] << "->" << _vertexs[desti] << ":" << weight << endl;}}//边数与比节点少一if (count == n - 1){cout << "构成最小生成树" << endl;return total;}else{cout << "无法构成最小生成树" << endl;return W();}
}

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

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

相关文章

XSS游戏

目录 XSS游戏-WarmupsMa Spaghet!JefffUgandan KnucklesRicardo MilosAh Thats HawtLigmaMafiaOk, BoomerWW3 XSS游戏-Warmups Ma Spaghet! 1. 尝试注入&#xff0c;输入aaaaaaaa 2. 显示在<h2>标签内3. 输入标签&#xff0c;添加onmouseover属性值为alert(1337)&…

【数据库】MySql基本引擎InnoDB、MyISAM、MEMORY、CSV、ARCHIVE(详细说明)

文章目录 如何选择引擎存储引擎的作用修改引擎方式InnoDBMyISAMMEMORYCSVARCHIVE总结 更多相关内容可查看 如何选择引擎 合理选择数据库存储引擎对于系统的性能、数据完整性、维护成本等方面都具有重要影响 高并发的事务性应用&#xff1a;选择 InnoDB。以读操作为主的应用&a…

【python】Python中小巧的异步web框架Sanic快速上手实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【SQL】连续出现的数字

目录 题目 分析 代码 题目 表&#xff1a;Logs ---------------------- | Column Name | Type | ---------------------- | id | int | | num | varchar | ---------------------- 在 SQL 中&#xff0c;id 是该表的主键。 id 是一个自增列。找出…

Langchain4J从AI Agent开始

前言 AI Agent是智能体的概念&#xff0c;以大模型为核心&#xff0c;集决策能力、记忆能力、工具调用能力为一体的智能体。 langchain框架是python写的&#xff0c;它是一个大语言模型智能框架&#xff0c;Langchain4J是langchain框架对应的java语言编写的。 langchain框架中…

Linux | vim编辑器的使用技巧:自动缩进、补全括号、光标定位、批量注释

文章目录 vim编辑器的使用技巧1、配置自动缩进、自动显示行号、自动补全括号2、光标定位3、批量注释、解除注释批量注释&#xff1a;批量解除注释 vim编辑器的使用技巧 1、配置自动缩进、自动显示行号、自动补全括号 打开vimrc配置文件 vim ~/.vimrc //如果没有编辑权限的&…

140页满分PPT | 一套完整的数字化转型+蓝图路线图规划项目案例

这份PPT文档是一个全面而深入的IT蓝图规划和实施路线图&#xff0c;为某大型制造企业的信息化建设和发展提供了详尽的框架和指导。 1. 业务战略与IT战略对接 在数字化转型过程中&#xff0c;应从企业的业务战略出发&#xff0c;强调了IT战略规划的重要性&#xff0c;指出IT战…

【计算机三级-数据库技术】数据库及数据库对象

数据库及数据库对象 第一节 创建及维护数据库 一、SQL server数据库分类 1&#xff09;系统数据库&#xff08;系统自动创建&#xff09;&#xff1a; master、msdb、tempdb、model、resource 2&#xff09;用户数据库 保存与用户业务有关的数据。 二、SQL server数据库组成…

一建证书哪个专业好?一建各专业含金量排行一览表

2024年一建考试将于9月7日、8日举行&#xff0c;距离现在已经不足五个月&#xff0c;今年一建考试大纲全新修订&#xff0c;教材也随之大改&#xff0c;加之一建考试难度较大&#xff0c;相信考生友友们都已经开启备考了吧&#xff1f; 近期也有小伙伴在跟小佑提问&#xff0c…

Is it possible to modify OpenAI environments?

题意&#xff1a;“是否可以修改 OpenAI 环境&#xff1f;” 问题背景&#xff1a; There are some things that I would like to modify in the OpenAI environments. If we use the Cartpole example then we can edit things that are in the class init function but with…

[数据集][目标检测]agvs仓储机器人检测数据集VOC+YOLO格式967张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;967 标注数量(xml文件个数)&#xff1a;967 标注数量(txt文件个数)&#xff1a;967 标注类别…

想要畅玩《黑神话:悟空》?上赞奇云工作站,仅需3步,直面天命!

全世界畅玩《黑神话&#xff1a;悟空》的时候 一批受害者们也迎来了属于他们的“九九八十一难” 这个时候&#xff0c;天命人才意识到 原来世界上最远的距离&#xff0c;不是天涯海角 而是《黑神话&#xff1a;悟空》就在我面前&#xff0c;旁边的电脑却带不动 作为被广泛视…

超维机器人在工业与能源领域的具身智能探索和应用

具身智能&#xff08;Embodied AI&#xff09;是指机器人能够通过其物理形态与环境的交互&#xff0c;进行感知、学习、决策和执行&#xff0c;从而完成复杂任务的能力。具身智能强调机器人不仅要具备感知环境和分析数据的能力&#xff0c;还要能够通过身体的行为和物理互动来适…

Cesium 视频纹理

Cesium 视频纹理 话不多说&#xff0c;直接上代码 <video id"video_dom"><source src"./video.mp4" type"video/mp4" /></video>var videoElement document.getElementById("video_dom");videoElement.play();vie…

145. 二叉树的后序遍历(递归法)

目录 一&#xff1a;题目&#xff1a; 二&#xff1a;代码&#xff1a; 三&#xff1a;结果&#xff1a; 一&#xff1a;题目&#xff1a; 给你一棵二叉树的根节点 root &#xff0c;返回其节点值的 后序遍历 。 二&#xff1a;代码&#xff1a; /*** Definition for a bin…

多进程和多线程基础概念LINUX

进程和程序的区别 程序是静态的&#xff0c;它是保存在磁盘上的指令的有序集合&#xff0c;没有任何执行的概念进程是一个动态的概念&#xff0c;它是程序执行的过程&#xff0c;包括了动态创建、调度和销毁的整个过程 并行&#xff1a;在 cpu 多核的支持下&#xff0c;实现物…

Linux--网络层 IP协议

目录 0.往期文章 1.IP基本概念 2. IP协议报头格式 3.网段划分 两种网段划分的方式 为什么要进行网段划分 4.特殊的IP 地址 5.IP 地址的数量限制 6.私有 IP 地址和公网 IP 地址*** NAT技术 认识公网 运营商扮演的角色 7.路由 8.16位标识&#xff0c;3为标志和13位…

SpringCloud之一注册中心(Eureka)

一、Eureka概述 Eureka是Netflix公司开源的一个服务注册与发现的中间组件。 在微服务架构系统之中&#xff0c;我们经常提三个角色&#xff1a;注册中心 (Register)、服务提供者(Provider)、服务消费者(Consumer)。 1.注册中心&#xff1a;服务提供者可以将服务发布到注册中心…

ClkLog常见问题-埋点集成篇Sec. 1

本篇主要解答ClkLog使用过程中【埋点集成】阶段的常见问题。 1.【指标项数据统计】 问&#xff1a;数据概览无法看到数据。 答&#xff1a;如果数据概览所有指标项都没有数据&#xff0c;则需要先检查埋点数据是否接收成功&#xff1b;如果只是会话相关数据&#xff08;访问次数…

《基于 Spark 的平替药品智能推荐方法》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…