利用栈和队列共同解决迷宫问题

文章目录

  • 什么是迷宫问题?
  • 如何解决迷宫问题?
    • DFS(深度优先搜索)
    • BFS(广度优先搜索)
  • 总结

什么是迷宫问题?

迷宫问题是一道经典的算法问题,旨在寻找一条从起点到终点的最短路径。通常迷宫由一个二维矩阵表示,其中0代表可通过的空地,1代表墙壁不可通过。

在此条件下,需要运用数据结构中的图算法如广度优先搜索(BFS)或深度优先搜索(DFS)等找出一条从起点到终点的最短路径。

  • 例如,以下就是一个迷宫:
111111111111111
100010001000001
101110101011001
100000101000101
111110111010101
100000001010101
101111111010101
101000001010001
101111101110101
100010000000101
111110111110101
100000100000101
111110101011101
100000001000001
111111111111111

(其中1表示墙壁不可通过,0表示可以通过的路径。起点为(1,1),终点为(15,15))


如何解决迷宫问题?

DFS(深度优先搜索)

对于深搜的基础知识,可以看我之前的博客:【算法基础】深搜

  • 使用深度优先搜索(DFS)算法来解决迷宫问题。具体思路如下:
  1. 选定起点,建立一个 visited 数组记录每个点是否已经遍历过。
  2. 对于起点开始向四周探索,如果能够到达未曾遍历的点,则继续向该点前进,直到无法前进或者到达出口。
  3. 在探索的过程中,将每个点的状态更新到visited数组中并标记已经访问。
  4. 如果走到了出口,则找到了一条可行路径;否则回溯到上一个节点分别从其它方向继续探索,直到所有状态被访问。

但是,深搜并不适合解决迷宫问题,甚至有时得到的解是错误的。
在实现时需要注意,DFS 算法虽然容易理解和实现,但是存在回溯次数多、时间复杂度高等缺点。因此,在实际应用中,需要仔细考虑算法的时间复杂度,并选择合适的数据结构和优化手段来提高程序效率。

  • 下面来看一个错误的案例:
    用二维数组来表示迷宫,每个元素表示该位置上的状态(例如墙壁、通道等)。其中,’O’表示可到达点,’X’表示不可到达点,’S’表示起始点,’E’表示终点。

  • 首先,初始数组的文件是这样的:
    在这里插入图片描述

  • 最短路径显然是从S直接到E,一步就可以解决问题,但事实是dfs走了27步。。。
    在这里插入图片描述

因为dfs的代码是这样的:

//ei, ej表示终点坐标,si, sj表示起点坐标,i, j表示现在的坐标 
int dfs(int ei, int ej, int si, int sj, int i, int j){
	int flag = 0;			//标记是否到达终点 
	a[i][j] = '.';			//标记此位置,表示已经访问过 
	if(i == ei && j == ej){
		flag = 1;
		if(cnt <= min_cnt) min_cnt = cnt;
	}
	
	//往右 
	if(flag != 1 && j+1 <= n && (a[i][j+1] == 'O' || a[i][j+1] == 'E')){
		push(s, i, j);
		if(dfs(ei ,ej, si, sj, i, j+1)==1){
			cnt++;
			flag = 1;
		}
	}
	//往下 
	if(flag != 1 && i+1 <= m && (a[i+1][j] == 'O' || a[i+1][j] == 'E')){
		push(s, i, j);
		if(dfs(ei, ej, si, sj, i+1, j) == 1){
			cnt++;
			flag = 1;
		}
	}
	//往左 
	if(flag != 1 && j-1 > 0 && (a[i][j-1] == 'O' || a[i][j-1] == 'E')){
		push(s, i, j);
		if(dfs(ei, ej, si, sj, i, j-1) == 1){
			cnt++;
			flag = 1;
		}
	}
	//往上
	if(flag != 1 && i-1 > 0 && (a[i-1][j] == 'O' || a[i-1][j] == 'E')){
		push(s, i, j);
		if(dfs(ei, ej, si, sj, i-1, j) == 1){
			cnt++;
			flag = 1;
		}
	} 
	
	
	if(flag != 1){
		a[i][j] == 'O';
		pop(s);
		cnt--;
	}
	return flag;
}

并没有找到最短路径,而是按照程序既定的顺序寻找终点。当然,我们也可以完善以下程序使寻路变得更加只能,但是这样写出来代码过于复杂。

BFS(广度优先搜索)

与BFS不同,BFS 算法的基本思路是从起点开始进行多层级别的搜索,逐渐向外扩展,直到找到终点或者所有状态被访问为止。

  • 具体步骤如下:
  1. 选定起点,以其为根节点建立一个 BFS 树,将其压入队列中。
  2. 对于每个节点,枚举所有可走的方向,生成该节点的子节点,并将其加入队列尾部。
  3. 在生成子节点时需要判断是否合法,如果不合法则忽略。这里建议使用 visited 数组记录每个点是否已经遍历过,以免出现死循环。
  4. 每次从队列头部取出一个节点并访问直到队列为空或找到终点
  5. 最终,如果想要打印出从起点到终点的路径,需要用到和BFS相配合。
  • 实现链栈的基本操作:栈在本实验中用于记录解决迷宫的路径,要实现基本的初始化、入栈、出栈和判空等操作。
  • 实现链式队列的基本操作:bfs算法借助队列实现迷宫路径的查找,所以要实现基本的初始化、入队、出队等操作。

BFS 算法不需要使用递归函数,因此比起 DFS 更容易实现和调试,并且可以找到最短路线。但是其空间复杂度较高,因此在实际应用中需要注意算法效率和内存占用情况。
其次,BFS算法是同时向外扩展多个路径,每一次扩展时,每一条路径都是相同的步数,所以首先到达终点的那条路径一定是步数最少的,也就是最短路径。

同样使用上面的迷宫表示方法,我们来看一下BFS的代码和性能:

// bfs算法,求出从起点到终点的最短路径,并输出路径中每一个点的坐标
int bfs(Point start, Point end) {
    Queue queue;
    initQueue(&queue);

    enQueue(&queue, start);
    vis[start.x][start.y] = 1;
	
	//bfs 
    while (queue.front != NULL) {
        Point current = deQueue(&queue);
		
		//已经到达终点 
        if (isEndPoint(maze, current)) {
            showPath(current, start, end);
            return 1;
        }

        // 上下左右四个方向搜索可达点
        Point up = {current.x - 1, current.y};
        Point down = {current.x + 1, current.y};
        Point left = {current.x, current.y - 1};
        Point right = {current.x, current.y + 1};
		
		//向上 
        if (up.x > 0 && isAccessible(maze, up) && !vis[up.x][up.y]) {
            enQueue(&queue, up);
            vis[up.x][up.y] = current.x * MAX_LEN + current.y;
        }
		//向下 
        if (down.x <= m && isAccessible(maze, down) && !vis[down.x][down.y]) {
            enQueue(&queue, down);
            vis[down.x][down.y] = current.x * MAX_LEN + current.y;
        }
		//向左 
        if (left.y > 0 && isAccessible(maze, left) && !vis[left.x][left.y]) {
            enQueue(&queue, left);
            vis[left.x][left.y] = current.x * MAX_LEN + current.y;
        }
		//向右 
        if (right.y <= n && isAccessible(maze, right) && !vis[right.x][right.y]) {
            enQueue(&queue, right);
            vis[right.x][right.y] = current.x * MAX_LEN + current.y;
        }
    }    
	
    printf("Error: no path found!\n");
    return 0;
}

具体代码可以从这里下载:

初始迷宫是这样的:
在这里插入图片描述
可以找到最短路径并输出:
在这里插入图片描述
如果需要以上的全部代码可以看本博客上传的代码包。


总结

迷宫问题是求解从起点到终点的路径,使得路径能够遍历迷宫所有有效格子的问题。这个问题在计算机科学中被广泛研究,有很多种算法可以解决。

  • 在深度优先搜索(DFS)算法中,我们需要递归地向前探索,直到找到终点或无法继续前进为止。DFS 算法比较容易实现,但是可能会导致出现死循环和非最优解。

  • 广度优先搜索(BFS)算法则采用分层扩展的方式,从起点开始进行多层级别的搜索,逐渐向外扩展,直到找到终点或者所有状态被访问为止。BFS 算法能够找到最短路径,并且不会出现死循环的情况,但是空间复杂度比 DFS 更高。

另外,A* 算法是一种启发式搜索算法,也可以很好的解决迷宫问题。它是基于估价函数对每个节点的代价进行评估,并根据代价来选择下一个扩展的节点。使用 A* 算法可以更快地找到最短路径,但是需要设计合适的估价函数。

除此之外,还有其他一些算法,如Dijkstra算法、IDA*算法等,均有自己的特点和应用场景。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

桶排序 — 计数排序和基数排序

计数排序 int类型数组&#xff0c;其中存的是员工的年龄。比如说16 - 150。对于这样的数据来讲&#xff0c;数据状况是受限的。此时如果将数组从小到大进行排序&#xff0c;该如果实现&#xff1f; 这个实现很简单&#xff0c;实现一个统计数组范围从 0 ~ 150&#xff0c;新数组…

Flume的安装和使用

安装Flume 1.1访问Flume的官网&#xff08;http://flume.apache.org/download.html&#xff09;&#xff0c;下载Flume安装apache-flume-1.9.0-bin.tar.gz。或者下载我的百度网盘资源。把安装文件解压缩到windows操作“D:\”目录下&#xff0c;然后执行如下命令测试是否安装成…

【JavaSE】Java基础语法(十六):抽象类

文章目录 1. 抽象类的概述2. 抽象类的特点3. 抽象类的实用价值4. 抽象类的案例 1. 抽象类的概述 当我们在做子类共性功能抽取时&#xff0c;有些方法在父类中并没有具体的体现&#xff0c;这个时候就需要抽象类了&#xff01; 在Java中&#xff0c;一个没有方法体的方法应该定义…

在Linux设备上让程序在任意目录都能执行

目录 0. 前言1. 编写代码2. 创建软链接3. 其他Linux文章 0. 前言 在Ubuntu上使用espidf中往往需要先设置环境变量&#xff0c;再执行export.sh&#xff0c;对环境装的乱七八糟的我造成了很大的不便我希望无论我在哪个目录&#xff0c;都能快速执行某个命令 我先是使用了编写b…

微信小程序开发实战 ⑨(TabBar)

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; 微信小程序 &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f4…

【Unittest】自动化测试框架核心要素

1、什么是Unittest框架&#xff1f; python自带一种单元测试框架 2、为什么使用UnitTest框架&#xff1f; >批量执行用例 >提供丰富的断言知识 >可以生成报告 3、核心要素&#xff1a; 1). TestCase&#xff08;测试用例&#xff09; 2). TestSuite(测试套件)…

WalkRE--刷图流程(超具体)

1、打开WalkRE软件&#xff0c;界面如下&#xff1a; 2、选择“根据模板新建工程”。操作如下&#xff1a; 3、导入数据。需要准备好dxf格式的CAD地形数据。操作如下&#xff1a; 在空白处右键&#xff0c;先关闭所有层&#xff08;大部分层在刷图时用不上&#xff0c;仅打开刷…

如何监控电动车充电桩能耗?

一 背景 随着新能源汽车的快速发展&#xff0c;像特斯拉、BYD、蔚来、小鹏和理想等品牌的电动汽车在我们的日常生活中越来越多了&#xff0c;可见电动汽车如今已逐渐被我们所认可了。同汽油车需要加油一样&#xff0c;电动汽车需要充电&#xff0c;如此一来&#xff0c;电动汽…

C++进阶 —— multimap

目录 一&#xff0c;multimap介绍 类pair 函数模板make_pair 二&#xff0c;multimap使用 一&#xff0c;multimap介绍 multimap是关联式容器&#xff0c;按照特定顺序存储键值对<key、value>&#xff0c;其中多个键值对之间的key可以重复&#xff1b;键key通常用于排…

供应链 | 在线平台的研究与思考(一):销售渠道与模式选择

封面图来源&#xff1a; https://www.pexels.com/zh-cn/photo/4968391/ 编者按 当前&#xff0c;电商平台主要采用两种销售模式&#xff1a;代理和分销。商家根据自身情况选择线上或线下渠道&#xff0c;而电商平台会根据不同的线上商家选择适当的分销模式。本期编者精选的两…

C++环形缓冲区设计与实现:从原理到应用的全方位解析

C环形缓冲区设计与实现&#xff1a;从原理到应用的全方位解析 一、环形缓冲区基础理论解析&#xff08;Basic Theory of Circular Buffer&#xff09;1.1 环形缓冲区的定义与作用&#xff08;Definition and Function of Circular Buffer&#xff09;1.2 环形缓冲区的基本原理&…

Smartbi“三步走”构建智慧经营分析平台,实现国有企业监管报送和数智化转型

01. 现状与痛点 — 一直以来&#xff0c;国资国企都是促进我国经济高速发展的领头羊&#xff0c;但近年来受疫情冲击和国际经济下行影响&#xff0c;国资企业经营面临较大压力&#xff0c;同时为实现国有企业高质量发展&#xff0c;国务院国资委下发一系列政策要求&#xff…

linuxOPS基础_vmware虚拟机安装及介绍

虚拟机概念 什么是虚拟机&#xff1f; 虚拟机&#xff0c;有些时候想模拟出一个真实的电脑环境&#xff0c;碍于使用真机安装代价太大&#xff0c;因此而诞生的一款可以模拟操作系统运行的软件。 虚拟机目前有2 个比较有名的产品&#xff1a;vmware 出品的vmware workstatio…

chatgpt赋能python:Python如何创建一个DataFrame

Python如何创建一个DataFrame 在数据科学和分析领域中&#xff0c;DataFrame是一种非常常见的数据结构。它类似于电子表格&#xff0c;可以存储和处理包含多个列和行的数据。在Python中&#xff0c;pandas库提供了DataFrame数据结构的支持。 什么是DataFrame&#xff1f; Da…

C++ list类成员函数介绍

目录 &#x1f914;list模板介绍&#xff1a; &#x1f914;特点&#xff1a; &#x1f914;list内存结构图解&#xff1a; &#x1f914; list的成员函数&#xff1a; &#x1f60a;list构造函数&#xff1a; &#x1f50d;代码示例&#xff1a; &#x1f50d;运行结果&…

RobotFramework接口测试方案

1. Robot FrameWork介绍 1.1 介绍 Robot Framework是用于验收测试和回归测试的通用测试自动化框架。它使用易于理解的表格数据语法&#xff0c;非常友好的实现了关键字驱动和数据驱动模式。它的测试功能可以通过使用Python或Java实现的测试库进行扩展&#xff0c;用户可以使用…

【JavaSE】Java基础语法(十七)

文章目录 1. final2. 代码块2.1 代码块概述2.2 代码块分类 1. final fianl关键字的作用 final代表最终的意思&#xff0c;可以修饰成员方法&#xff0c;成员变量&#xff0c;类 final修饰类、方法、变量的效果 fianl修饰类&#xff1a;该类不能被继承&#xff08;不能有子类&a…

软件测试基础概念

目录 软件测试的生命周期如何描述一个bug如何定义bug的级别bug的生命周期产生争执怎么办&#xff08;处理人际关系&#xff09;如何开始第一次测试测试的执行和bug管理如何发现更多的bug 软件测试的生命周期 需求分析 – 测试计划 – 测试设计、测试开发 – 测试执行 – 测试评…

界面控件DevExpress WinForms全新的UI模板,解决各种业务线需求!

去年秋天DevExpress官方发布了一个新的 WinForms UI模板预览版&#xff08;第一个EAP只提供给DevExpress宇宙版激活的用户&#xff09; &#xff0c;这些精炼的、随时可用的“模板”旨在启动表单设计/开发过程。有了这个模板&#xff0c;用户可以创建/交付现成的UI解决方案&…

Jenkins——maven 插件配置

文章目录 一、Maven 的集成二、在执行job的机器上安装好maven三、下载 maven 插件四、配置全局工具五、Maven 相关使用1、新建 job2、自由风格 job 中命令行使用 mvn 命令3、构建操作 一、Maven 的集成 在 Jenkins 上构建 Java 项目时需要使用 Maven 来进行构建打包 二、在执…