写给妹妹的编程札记 5 - 搜索: 迷宫问题 - 广度优先搜索

2024-05-24 04:58

本文主要是介绍写给妹妹的编程札记 5 - 搜索: 迷宫问题 - 广度优先搜索,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        让我们也使用广度优先搜索来解决一下迷宫问题,可以对比一下《写给妹妹的编程札记 4 - 搜索: 迷宫问题 - 深度优先搜索》。

        如在《写给妹妹的编程札记 3 - 穷举: 深度优先搜索/广度优先搜索》中描述的广度优先搜索, 对一个简单的例子,我们手动进行一遍迷宫遍历。每次找到队首的搜索状态,把从这个状态开始的全部状态加入队列。



        广度优先搜索, 我们需要一个队列来维护访问过的搜索状态。 对于每个搜索状态,我们记录该状态对应迷宫中的位置currX/currY, 从上一个状态怎么过来的: prevDir, 上一个状态在队列中的位置prevState。 后面两个信息prevDir/prevState主要是为了反向找到路径。

typedef struct tagMazeState {int currX;int currY;int prevDir;int prevState;
} MazeState;


        除了新创建的队列,跟深度优先搜索一样, 我们需要记录一个位置是否访问过 - isVisited数组。

void Search(char** maze, int row, int col)
{    bool** isVisited;isVisited = new bool*[row];for(int i = 0; i < row; i++) {isVisited[i] = new bool[col];memset(isVisited[i], 0, sizeof(bool) * col);}MazeState* queue = new MazeState[row * col];int queueHead = 0;int queueTail = 0;MazeState origin;origin.currX     = 0;origin.currY     = 0;origin.prevDir   = -1;origin.prevState = -1;// 判断起点是否为空位if (maze[0][0] != '.') {printf("Invalid maze. Origin maze[0][0] is blocked!\n");return;}// 判断是否已经到达终点if (origin.currX == row - 1 && origin.currY == col - 1) {printf("Already arrive target position!\n");return;}queue[queueTail++] = origin;isVisited[0][0] = 1;BFS(maze, row, col, isVisited, queue, queueHead, queueTail);for(int i = 0; i < row; i++) delete[] isVisited[i];delete[] isVisited;delete[] queue;
}


        有了搜索状态队列的支持, 广度优先搜索的代码就很简单了:

void BFS(char** maze, int row, int col, bool** isVisited, MazeState* queue, int queueHead, int queueTail)
{// 判断队列是否为空while (queueHead < queueTail) {// 取出队首的搜索状态int currX = queue[queueHead].currX;int currY = queue[queueHead].currY;printf("queueHead: (%d, %d)\n", currX, currY);// 检查从这个状态开始的所有能够到达的状态 (广度优先搜索)for (int k = 0; k < 4; k++) {int nextX = currX + direction[k][0];int nextY = currY + direction[k][1];// 检查保证新位置在迷宫内,没有离开迷宫, 且是空位(不能走到墙里面)if ((nextX >= 0) && (nextX < row) && (nextY >= 0) && (nextY < col) && (maze[nextX][nextY] == '.')) {// 找到可以走的方向后, 需要判断这个新的位置是否已经走过,如果已经走过,我们继续会走就出现环路if (!isVisited[nextX][nextY]) {// 标记搜索访问过isVisited[nextX][nextY] = 1;// 没有走过的新位置,加入队列 (广度优先搜索)MazeState nextState;nextState.currX     = nextX;nextState.currY     = nextY;nextState.prevDir   = k;nextState.prevState = queueHead;queue[queueTail++]  = nextState;// 一旦发现目标状态到达的话, 说明已经找到最短路径,输出if (nextX == row - 1 && nextY == col - 1) {// 输出最短路径DisplayPath(queue, queueTail - 1);// 终止搜索return;}}}}// 队首的搜索状态已经完成,从队列中删除 (没有实际删除,还在queue数组中,只是不在逻辑队列中[queueHead, queueTail) )queueHead++;}
}


        跟深度优先搜索不同的是,我们采取广度优先,多条路径同时进行,不能只使用一个path数组保存当前的路径。 对应于搜索状态队列中的每一个状态, 其实都对应着一条从起点开始到该状态的一条路径。为了反向找到这条路径,搜索状态队列中的每个状态都已经记录了从哪个状态过来的,怎么过来的。 我们需要反向找到这条路径:


// 输出从起点开始到(queue[targetPositionIndex].currX, queue[targetPositionIndex].currY)的路径
void DisplayPath(MazeState* queue, int targetPositionIndex)
{// 终止条件。 起点的前一个状态index为:-1if (targetPositionIndex < 0) {return;}// 前一个状态int previousPositionIndex = queue[targetPositionIndex].prevState;// 从前一个状态怎么过来的int previousDirection     = queue[targetPositionIndex].prevDir;// 先输出从起点到前一个状态的路径DisplayPath(queue, previousPositionIndex);// 再输出从前一个状态到当前状态的路径switch(previousDirection) {case 0: printf("N"); break;case 1: printf("S"); break;case 2: printf("W"); break;case 3: printf("E"); break;}
}




这篇关于写给妹妹的编程札记 5 - 搜索: 迷宫问题 - 广度优先搜索的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/997439

相关文章

MySQL主从同步延迟问题的全面解决方案

《MySQL主从同步延迟问题的全面解决方案》MySQL主从同步延迟是分布式数据库系统中的常见问题,会导致从库读取到过期数据,影响业务一致性,下面我将深入分析延迟原因并提供多层次的解决方案,需要的朋友可... 目录一、同步延迟原因深度分析1.1 主从复制原理回顾1.2 延迟产生的关键环节二、实时监控与诊断方案

SQLyog中DELIMITER执行存储过程时出现前置缩进问题的解决方法

《SQLyog中DELIMITER执行存储过程时出现前置缩进问题的解决方法》在SQLyog中执行存储过程时出现的前置缩进问题,实际上反映了SQLyog对SQL语句解析的一个特殊行为,本文给大家介绍了详... 目录问题根源正确写法示例永久解决方案为什么命令行不受影响?最佳实践建议问题根源SQLyog的语句分

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

MyBatis模糊查询报错:ParserException: not supported.pos 问题解决

《MyBatis模糊查询报错:ParserException:notsupported.pos问题解决》本文主要介绍了MyBatis模糊查询报错:ParserException:notsuppo... 目录问题描述问题根源错误SQL解析逻辑深层原因分析三种解决方案方案一:使用CONCAT函数(推荐)方案二:

Redis 热 key 和大 key 问题小结

《Redis热key和大key问题小结》:本文主要介绍Redis热key和大key问题小结,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、什么是 Redis 热 key?热 key(Hot Key)定义: 热 key 常见表现:热 key 的风险:二、

IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤及问题解决

《IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决》:本文主要介绍IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决,本文分步骤结合实例给大... 目录步骤 1:创建 Maven Web 项目步骤 2:添加 Spring MVC 依赖1、保存后执行2、将新的依赖

Spring 中的循环引用问题解决方法

《Spring中的循环引用问题解决方法》:本文主要介绍Spring中的循环引用问题解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录什么是循环引用?循环依赖三级缓存解决循环依赖二级缓存三级缓存本章来聊聊Spring 中的循环引用问题该如何解决。这里聊

Spring Boot中JSON数值溢出问题从报错到优雅解决办法

《SpringBoot中JSON数值溢出问题从报错到优雅解决办法》:本文主要介绍SpringBoot中JSON数值溢出问题从报错到优雅的解决办法,通过修改字段类型为Long、添加全局异常处理和... 目录一、问题背景:为什么我的接口突然报错了?二、为什么会发生这个错误?1. Java 数据类型的“容量”限制

关于MongoDB图片URL存储异常问题以及解决

《关于MongoDB图片URL存储异常问题以及解决》:本文主要介绍关于MongoDB图片URL存储异常问题以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录MongoDB图片URL存储异常问题项目场景问题描述原因分析解决方案预防措施js总结MongoDB图