算法学习(5)-图的遍历

目录

什么是深度和广度优先

图的深度优先遍历-城市地图

图的广度优先遍历-最少转机


什么是深度和广度优先

         使用深度优先搜索来遍历这个图的过程具体是:

  1. 首先从一个未走到过的顶点作为起始顶点, 比如以1号顶点作为起点。
  2. 沿1号顶点的边去尝试访问其它未走到过的顶点, 首先发现2 号顶点还没有走到过, 于是来到了2 号顶点。
  3. 再以2 号顶点作为出发点继续尝试访问其它未走到过的顶点, 这样又来到了4号顶点。
  4. 再以4 号顶点作为出发点继续尝试访问其它未走到过的顶点。
  5. 但是, 此时沿4号顶点的边, 已经不能访问到其它未走到过的顶点了, 所以要返回到2号顶点。
  6. 返回到2号顶点后, 发现沿2 号顶点的边也不能再访问到其它未走到过的顶点。因此还需要继续返回到1号顶点。
  7. 再继续沿1号顶点的边看看还能否访问到其它未走到过的顶点。
  8. 此时又会来到3号顶点, 再以3号顶点作为出发点继续访问其它未走到过的顶点, 千是又来到5号顶点。
  9. 到此, 所有顶点都走到过了, 遍历结束。

        深度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点;没有未访问过的顶点时, 则回到上一个顶点, 继续试探访问别的顶点, 直到所有的顶点都被访问过。

        显然, 深度优先遍历是沿着图的某一条分支遍历直到末端, 然后回溯, 再沿着另一条进行同样的遍历, 直到所有的顶点都被访问过为止。那这一过程如何用代码来实现呢?在讲代码实现之前我们先来解决如何存储一个图的问题。最常用的方法是使用一个二维数组e来存储, 如下。

 

        上图二维数组中第i行第j列表示的就是顶点 i 到顶点 j 是否有边。1表示有边, ∞表示
没有边, 这里我们将自己到自己(即i等于j)设为0。我们将这种存储图的方法称为图的邻
接矩阵存储法。
        注意观察会发现这个二维数组是沿主对角线对称的, 因为上面这个图是无向图。所谓无向阳指的就是图的边没有方向, 例如边1-5表示, 1号顶点可以到5号顶点, 5号顶点也可以到1号顶点。
        接下来要解决的问题就是如何用深度优先搜索来实现遍历了。 

void dfs(int cur) { // cur是当前所在的顶点编号printf("%d. ", cur);sum++; // 每访问一个顶点s就加1if (sum == n) return; // 所有的顶点都已经访问过则直接退出for (i = 1; i <= n; i++) { // 从1号顶点到n号顶点依次尝试,看哪些顶点与当前顶点cur有边相连// 判断当前顶点cur到顶点i是否有边,并判断顶点i是否已访问过if (e[cur][i] == 1 && book[i] == 0) {// 标记顶点i已经访问过book[i] = 1;// 从顶点i再出发继续遍历dfs(i);}}return;
}

        在上面的代码中变揽cur存储的是当前正在遍历的顶点, 二维数组e存储的就是图的边(邻接矩阵), 数组book用来记录哪些顶点已经访问过, 变揽sum用来记录已经访问过多少个顶点, 变证n存储的是图的顶点的总个数。完整代码如下。

#include <stdio.h>int book[101], sum, n, e[101][101];void dfs(int cur) { // cur是当前所在的顶点编号int i;printf("%d ", cur);sum++; // 每访问一个顶点,sum就加1if (sum == n) return; // 所有的顶点都已经访问过则直接退出for (i = 1; i <= n; i++) { // 从1号顶点到n号顶点依次尝试,看哪些顶点与当前顶点cur有边相连// 判断当前顶点cur到顶点i是否有边, 并判断顶点i是否已访问过if (e[cur][i] == 1 && book[i] == 0) {// 标记顶点i已经访问过book[i] = 1;// 从顶点i再出发继续遍历dfs(i);}}return;
}int main() {int i, j, m, a, b;scanf("%d %d", &n, &m);// 初始化二维矩阵for (i = 1; i <= n; i++) {for (j = 1; j <= n; j++) {if (i == j)e[i][j] = 0;elsee[i][j] = 99999999; // 我们这里假设999999999为正无穷}}// 读入顶点之间的边for (i = 1; i <= m; i++) {scanf("%d %d", &a, &b);e[a][b] = 1;e[b][a] = 1; // 这里是无向图,所以需要将e[b][a]也赋为1}// 从1号城市出发book[1] = 1; // 标记1号顶点已访问dfs(1); // 从1号顶点开始遍历getchar();getchar();return 0;
}

        广度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点, 然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过, 遍历结束。

        代码实现如下:

#include <stdio.h>int main() {int i, j, n, m, a, b, cur;int book[101] = {0}; // 使用数组初始化语法将book数组初始化为全0int e[101][101];int que[10001], head = 0, tail = 0; // 将队列初始化为0scanf("%d %d", &n, &m);// 初始化二维矩阵for (i = 1; i <= n; i++) {for (j = 1; j <= n; j++) {if (i == j) {e[i][j] = 0;}else {e[i][j] = 99999999; // 假设999999999为正无穷}}}// 读入顶点之间的边for (i = 1; i <= m; i++) {scanf("%d %d", &a, &b);e[a][b] = 1;e[b][a] = 1; // 无向图,需要双向赋值}// 从1号顶点出发,将1号顶点加入队列que[tail] = 1;tail++;book[1] = 1; // 标记1号顶点已访问// 当队列不为空时循环while (head < tail) {cur = que[head]; // 当前正在访问的顶点编号// 从1~n依次尝试for (i = 1; i <= n; i++) {// 判断从顶点cur到顶点i是否有边,且顶点i是否已经访问过if (e[cur][i] == 1 && book[i] == 0) {// 如果从顶点cur到顶点i有边,并且顶点i没有被访问过,则将顶点i入队que[tail] = i;tail++;book[i] = 1; // 标记顶点i已访问}}// 如果tail大于n,则所有顶点都已经被访问过,退出循环if (tail > n) {break;}head++; // 顶点扩展结束后,head++,继续往下扩展}// 输出队列中的顶点编号for (i = 0; i < tail; i++) {printf("%d ", que[i]);}getchar();getchar();return 0;
}

图的深度优先遍历-城市地图

         我们可以创建一个5*5的邻接矩阵,如下图:

        用book数组记录哪些城市已经走过,用全局变量min来更新每次找到的路径的最小值,代码实现如下:

#include <stdio.h>
int min = 99999999, book[101], n, e[101][101]; // 我们这里假设999999999为正无穷// cur是当前所在的城市编号,dis是当前已经走过的路程
void dfs(int cur, int dis) {int j;// 如果当前走过的路程已经大于之前找到的最短路,则没有必要再往下尝试了,立即返回if (dis > min)return;if (cur == n)  // 判断是否到达了目标城市{if (dis < min) {min = dis;  // 更新最小值return;}}for (j = 1; j <= n; j++) { // 从1号城市到n号城市依次尝试// 判断当前城市cur到城市j是否有路,并判断城市j是否在已走过的路径中if (e[cur][j] != 99999999 && book[j] == 0) {book[j] = 1;  // 标记城市j已经在路径中dfs(j, dis + e[cur][j]);  // 从城市j再出发,继续寻找目标城市book[j] = 0;  // 之前一步探索完毕之后,取消对城市j的标记}}
}int main() {int i, j, m, a, b, c;scanf("%d %d", &n, &m);// 初始化二维矩阵for (i = 1; i <= n; i++)for (j = 1; j <= n; j++)if (i == j)e[i][j] = 0;elsee[i][j] = 99999999;// 读入城市之间的道路for (i = 1; i <= m; i++) {scanf("%d %d %d", &a, &b, &c);e[a][b] = c;}// 从1号城市出发book[1] = 1;  // 标记1号城市已经在路径中dfs(1, 0);  // 1表示当前所在的城市编号,0表示当前已经走过的路程printf("%d\n", min);  // 打印1号城市到目标城市的最短路径getchar();getchar();return 0;
}


图的广度优先遍历-最少转机

 

#include <stdio.h>struct note {int x;  // 城市编号int s;  // 转机次数
};int main() {struct note que[2501];// 初始化int e[51][51] = {0}, book[51] = {0};int head, tail;int i, j, n, m, a, b, cur, start, end, flag = 0;scanf("%d %d %d %d", &n, &m, &start, &end);// 初始化二维矩阵for (i = 1; i <= n; i++) {for (j = 1; j <= n; j++) {if (i == j)e[i][j] = 0;elsee[i][j] = 99999999;}}// 读入城市之间的航班for (i = 1; i <= m; i++) {scanf("%d %d", &a, &b);// 注:这里是无向图e[a][b] = 1;e[b][a] = 1;}// 队列初始化head = 1;tail = 1;// 从start号城市出发,将start号城市加入队列que[tail].x = start;que[tail].s = 0;tail++;book[start] = 1;  // 标记start号城市已在队列中// 当队列不为空的时候循环while (head < tail) {cur = que[head].x;  // 当前队列中首城市的编号for (j = 1; j <= n; j++) {  // 从1~n依次尝试// 从城市cur到城市j是否有航班并且判断城市j是否已经在队列中if (e[cur][j] != 99999999 && book[j] == 0) {// 如果从城市cur到城市j有航班并且城市j不在队列中,则将城市j入队que[tail].x = j;que[tail].s = que[head].s + 1;  // 转机次数+1tail++;// 标记城市j已经在队列中book[j] = 1;// 如果到达目标城市,停止扩展,任务结束,退出循环if (que[tail].x == end) {// 注意下面两句话的位置千万不要写颠倒了flag = 1;break;}}}if (flag == 1)break;head++;  // 注意这地方,千万不要忘记当一个点扩展结束后,head++才能继续扩展}// 打印队列中末尾最后一个(目标城市)的转机次数// 注意:tail是指向队列队尾(即最后一位)的下一个位置,所以这里要-1printf("%d\n", que[tail - 1].s);getchar();getchar();return 0;
}

         当然也可以使用深度优先搜索解决, 但是这里用广度优先搜索会更快。广度优先搜索更
加适用于所有边的权值相同的情况。

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

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

相关文章

提升编码技能:学习如何使用 C# 和 Fizzler 获取特价机票

引言 五一假期作为中国的传统节日&#xff0c;也是旅游热门的时段之一&#xff0c;特价机票往往成为人们关注的焦点。在这个数字化时代&#xff0c;利用爬虫技术获取特价机票信息已成为一种常见的策略。通过结合C#和Fizzler库&#xff0c;我们可以更加高效地实现这一目标&…

2024年---蓝桥杯网络安全赛道部分WP

一、题目名称&#xff1a;packet 1、下载附件是一个流量包 2、用wireshark分析&#xff0c;看到了一个cat flag的字样 3、追踪http数据流&#xff0c;在下面一行看到了base64编码。 4、解码之后得到flag 二、题目名称&#xff1a;cc 1、下载附件&#xff0c;打开是一个html …

Docker构建LNMP部署WordPress

前言 使用容器化技术如 Docker 可以极大地简化应用程序的部署和管理过程&#xff0c;本文将介绍如何利用 Docker 构建 LNMP 环境&#xff0c;并通过部署 WordPress 来展示这一过程。 目录 一、环境准备 1. 项目需求 2. 安装包下载 3. 服务器环境 4. 规划工作目录 5. 创…

CAPS Wizard for Mac:打字输入辅助应用

CAPS Wizard for Mac是一款专为Mac用户设计的打字输入辅助应用&#xff0c;以其简洁、高效的功能&#xff0c;为用户带来了全新的打字体验。 CAPS Wizard for Mac v5.3激活版下载 该软件能够智能预测用户的输入内容&#xff0c;实现快速切换和自动大写锁定&#xff0c;从而大大…

OmniReader Pro for Mac:强大且全面的阅读工具

OmniReader Pro for Mac是一款专为Mac用户设计的强大且全面的阅读工具&#xff0c;它集阅读、编辑、管理等多种功能于一身&#xff0c;为用户提供了卓越的阅读体验。 OmniReader Pro for Mac v2.9.5激活版下载 该软件支持多种文件格式的阅读&#xff0c;包括PDF、Word、Excel、…

pycharm配置wsl开发环境(conda)

背景 在研究qanything项目的过程中&#xff0c;为了进行二次开发&#xff0c;需要在本地搭建开发环境。然后根据文档说明发现该项目并不能直接运行在windows开发环境&#xff0c;但可以运行在wsl环境中。于是我需要先创建wsl环境并配置pycharm。 wsl环境创建 WSL是“Windows Su…

新时代写作与互动:《一本书讲透 Elasticsearch》读者群的创新之路

1、《一本书讲透 Elasticsearch》销售最近进展汇报 给大家同步一下《一本书讲透 Elasticsearch》图书的进展情况&#xff0c;本周五&#xff08;2024年4月26日&#xff09;&#xff0c;出版社编辑老师反馈图书相关销量进展&#xff1a; 预计全网销量 1000 册&#xff0c;发货量…

OpenHarmony语言基础类库【@ohos.xml (xml解析与生成)】

将XML文本转换为JavaScript对象、以及XML文件生成和解析的一系列接口。 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import xml from ohos.xml; XmlSerializer XmlSerializer接口…

FPGA实现图像处理之【直方图均衡-寄存器版】

FPGA实现直方图统计 一、图像直方图统计原理 直方图的全称为灰度直方图&#xff0c;是对图像每一灰度间隔内像素个数的统计。即对一张图片中每隔二灰度值的像素数量做统计&#xff0c;然后以直方图的形式展现出来。图下的亮暗分布在直方图中就可以一目了然&#xff0c;直方图…

Spark核心名词解释与编程

Spark核心概念 名词解释 1)ClusterManager&#xff1a;在Standalone(上述安装的模式&#xff0c;也就是依托于spark集群本身)模式中即为Master&#xff08;主节点&#xff09;&#xff0c;控制整个集群&#xff0c;监控Worker。在YARN模式中为资源管理器ResourceManager(国内…

paddlehub的简单应用

1、下载安装 pip install paddlehub -i https://pypi.tuna.tsinghua.edu.cn/simple 报错&#xff1a; Collecting onnx<1.9.0 (from paddle2onnx>0.5.1->paddlehub)Using cached https://pypi.tuna.tsinghua.edu.cn/packages/73/e9/5b953497c0e36df589fc60cc6c6b35…

Redux数据流架构

Redux的难点是理解它对于数据修改的规则, 下图动态展示了在整个数据的修改中&#xff0c;数据的流向 Redux代码被分为三个核心的概念&#xff0c;三个概念分别是: state: 一个对象 存放着我们管理的数据action: 一个对象 用来描述你想怎么改数据reducer: 一个函数 根据action的…

万兆以太网MAC设计(11)完整UDP协议栈仿真

文章目录 前言一、模块接口二、IP模块与ARP模块之间的联系三、整体协议栈仿真总结&#xff1a; 前言 目前除了巨帧处理逻辑之外&#xff0c;所有的准备工作都已经结束了&#xff0c;先进行整体的功能验证。 一、模块接口 所有模块接口皆采用AXIS数据流的形式&#xff0c;其中…

用Jenkins实现cherry-pick多个未入库的gerrit编译Android固件

背景: 在做Android固件开发的时候,通常我们可以利用gerrit-trigger插件,开发者提交一笔的时候自动触发jenkins编译,如果提交的这一笔的编译依赖其他gerrit才能编译过,我们可以在commit message中加入特殊字段,让jenkins在编译此笔patch的时候同时抓取依赖的gerrit代码下…

java后端项目:视积分抽奖平台

一、项目背景: 本次抽奖系统实现是在视频中内置一个线上活动抽奖系统,奖品是在一个时间段区间内均匀发布,用户可以在这个时间段内参与抽奖。 二、项目架构 活动抽奖平台采用微服务架构来完成,在功能上实现拆分为用户、网关、以及抽奖微服务,其中用户、网关是后台项目通…

三. TensorRT基础入门-TensorRT内部的优化模块

目录 前言0. 简述1.TensorRT的优化策略2. Layer Fusion3. Kernel Auto-Tuning4. Quantization总结参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习课程第三章—TensorRT 基础入…

C++ | 类和对象(上)

目录 什么是类 类的介绍 struct在两种语言中的有何区别 私有变量命名注意点 类的作用域 类的声明定义分离 类的访问限定符 封装 类的实例化 类对象的存储 this指针 一道this指针相关的王炸题&#xff1a; 结语 什么是类 类的介绍 我们举一个日常生活中的例子&…

罗宾斯《管理学》第15版笔记/课后习题/考研真题答案

第Ⅰ篇 管理导论 第1章 工作场所中的管理者和你 1.1 知识结构导图 1.2 考点难点归纳 1.3 课后习题详解 1.4 考研真题详解 附加模块一 管理史 知识结构导图 考点难点归纳 课后习题详解 考研真题详解 第2章 决 策 2.1 知识结构导图 2.2 考点难点归纳 2.3 课后习题详解…

C之·标准库<string.h>

系列文章目录 文章目录 前言一、字符串分割函数1.strtok()2. 总结 前言 <stdlib.h> 是C语言中的一个头文件&#xff0c;提供了一系列用于操作字符串的函数。例如查找子字符串、拼接字符串、比较字符串等等。为了方便开发者进行字符串操作&#xff0c;C语言提供了一个标准…

python爬虫学习-------scrapy的第一部分(二十九天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…