[C/C++] -- 搜索迷宫路径

news2024/11/26 20:22:08

DFS(深度优先搜索)和BFS(广度优先搜索)是两种常用的图遍历算法,它们在搜索图或树中的节点时有着不同的策略和特点。

  1. 深度优先搜索 (DFS):

    • 在DFS中,从起始节点开始,沿着一条路径尽可能深地搜索,直到到达叶子节点或者无法继续搜索为止,然后回溯到上一个节点,选择另一条路径继续搜索,直到所有节点都被访问。
    • DFS通常使用递归或者来实现,递归是自然而直观的实现方式,而使用栈可以避免递归的潜在问题(如栈溢出)。
    • DFS的优点是在搜索过程中不需要记录所有访问过的节点,因此占用的空间较少。
    • 适用于寻找深度路径,比如解决迷宫问题、寻找图的连通分量等。
  2. 广度优先搜索 (BFS):

    • 在BFS中,从起始节点开始,首先访问起始节点的所有邻居节点,然后依次访问这些邻居节点的邻居节点,以此类推,直到所有节点都被访问。
    • BFS通常使用队列来实现,保证了节点的访问顺序是按照距离起始节点的距离逐层递增的。
    • BFS的优点是可以找到起始节点到目标节点的最短路径,而且在无权图中具有最优性(即找到的第一个解就是最短路径)。
    • 适用于寻找最短路径,比如在迷宫中找到最短路径、在社交网络中找到两个人之间的最短关系链等。

1.深度优先遍历DFS

设计一个程序,能够对给定的迷宫进行路径搜索,并输出一条从起点到终点的路径。具体来说,程序需要实现以下功能:

  1. 接受用户输入的迷宫地图,包括迷宫的行数和列数,以及每个格子的状态(0 表示可通行,1 表示不可通行)。
  2. 使用深度优先搜索算法(DFS)对迷宫进行搜索,找到从起点到终点的一条路径。
  3. 输出搜索到的路径。

入栈以后,然后我们查看栈顶元素,是(0,0)这个节点。栈现在不为空,取栈顶元素,先看它右边能不能走,能走的话,就一直向右走,它右边是0,就入栈了。

_pMaze[0][0]._val == 1
    return;
_stack. Push(_pMaze[0][0]);//左上角节点入栈 
while (!_stack. Empty())//栈不为空 
	Node top = _stack. Top();

 如果栈顶元素的右边可以走的话,我们要把当前节点的右方向改成不能走,把右边节点的左方向改成不能走。因为不能走回头路,而且因为路子走不通回退后也不能继续走相同的死路。

//往右方向寻找
			if (_pMaze[x][y]._state[RIGHT] == YES)
			{
				_pMaze[x][y]._state[RIGHT] = NO;
				_pMaze[x][y + 1]._state[LEFT] = NO;
				_stack.push(_pMaze[x][y + 1]);
				continue;

深度遍历入栈后,不用判断之前节点的方向。continue后,重新取栈顶元素,继续进行判断。同理,如果右边不能走,下边可以走的话:

//往下方向寻找
			if (_pMaze[x][y]._state[DOWN] == YES)
			{
				_pMaze[x][y]._state[DOWN] = NO;
				_pMaze[x + 1][y]._state[UP] = NO;
				_stack.push(_pMaze[x + 1][y]);
				continue;
			}

 同理:左方向

//往左方向寻找
			if (_pMaze[x][y]._state[LEFT] == YES)
			{
				_pMaze[x][y]._state[LEFT] = NO;
				_pMaze[x][y - 1]._state[RIGHT] = NO;
				_stack.push(_pMaze[x][y - 1]);
				continue;
			}

如果栈顶元素判断完四个方向都不能走,就是到死路了,就把栈顶元素出栈。
然后再取栈顶元素,进行判断,如果它的4个方向都不能走,就出栈,如果栈为空,则迷宫无通路。如果有方向能走,就继续走下去。以此类推下去。但是,都要判断一下此节点是不是右下角的节点,如果是,就是找到通路了。

//已经找到右下角出口得迷宫路径
			if (x == _row - 1 && y == _col - 1)
			{
				return;
			}

 完整代码:

#include <iostream>
#include <stack>
using namespace std;

//定义迷宫每一个节点的四个方向
const int RIGHT = 0;//右 
const int DOWN = 1;//下 
const int LEFT = 2;//左 
const int UP = 3;//上 

//迷宫每一个节点方向的数量
const int WAY_NUM = 4;

//定义节点行走状态
const int YES = 4;//当前方向可以走 
const int NO = 5;//当前方向不能走 

//迷宫
class Maze
{
public:
	//初始化迷宫,根据用户输入的行列数,生成存储迷宫路径信息的二维数组
	Maze(int row, int col)
		:_row(row)
		, _col(col)
	{
		_pMaze = new Node*[_row];//注意元素类型是Node 
		for (int i = 0; i < _row; ++i)
		{
			_pMaze[i] = new Node[_col];
		}
	}

	//初始化迷宫路径节点信息
	void initNode(int x, int y, int val)
	{
		_pMaze[x][y]._x = x;
		_pMaze[x][y]._y = y;
		_pMaze[x][y]._val = val;
		//节点四个方向默认的初始化都为不能走 
		for (int i = 0; i < WAY_NUM; ++i)
		{
			_pMaze[x][y]._state[i] = NO;
		}
	}

	//初始化迷宫0节点四个方向的行走状态信息 当前节点右下左上,如果是0,改成可以走 
	void setNodeState()
	{
		for (int i = 0; i < _row; ++i)
		{
			for (int j = 0; j < _col; ++j)
			{
				if (_pMaze[i][j]._val == 1)
				{
					continue;//不用调整了,因为走不到值为1的节点 
				}
				
                //j不能取到最后一列,不然就判断越界了 
				if (j < _col - 1 && _pMaze[i][j + 1]._val == 0)
				//逻辑&,是先计算左边的表达式,左边如果是false,右边就不用计算了 
				{
					_pMaze[i][j]._state[RIGHT] = YES;
				}

				if (i < _row - 1 && _pMaze[i + 1][j]._val == 0)
				{
					_pMaze[i][j]._state[DOWN] = YES;
				}
				
                //j不用取第一类,因为本身就不能走 
				if (j > 0 && _pMaze[i][j - 1]._val == 0)
				{
					_pMaze[i][j]._state[LEFT] = YES;
				}

				if (i > 0 && _pMaze[i - 1][j]._val == 0)
				{
					_pMaze[i][j]._state[UP] = YES;
				}
			}
		}
	}

	//深度搜索迷宫路径
	void searchMazePath()
	{
		if (_pMaze[0][0]._val == 1)
		{
			return;
		}
		_stack.push(_pMaze[0][0]);//左上角节点入栈 

		while (!_stack.empty())//栈不为空 
		{
			Node top = _stack.top();
			int x = top._x;
			int y = top._y;

			//已经找到右下角出口得迷宫路径
			if (x == _row - 1 && y == _col - 1)
			{
				return;
			}

			//往右方向寻找
			if (_pMaze[x][y]._state[RIGHT] == YES)
			{
				_pMaze[x][y]._state[RIGHT] = NO;
				_pMaze[x][y + 1]._state[LEFT] = NO;
				_stack.push(_pMaze[x][y + 1]);
				continue;
			}

			//往下方向寻找
			if (_pMaze[x][y]._state[DOWN] == YES)
			{
				_pMaze[x][y]._state[DOWN] = NO;
				_pMaze[x + 1][y]._state[UP] = NO;
				_stack.push(_pMaze[x + 1][y]);
				continue;
			}

			//往左方向寻找
			if (_pMaze[x][y]._state[LEFT] == YES)
			{
				_pMaze[x][y]._state[LEFT] = NO;
				_pMaze[x][y - 1]._state[RIGHT] = NO;
				_stack.push(_pMaze[x][y - 1]);
				continue;
			}

			//往上方向寻找
			if (_pMaze[x][y]._state[UP] == YES)
			{
				_pMaze[x][y]._state[UP] = NO;
				_pMaze[x - 1][y]._state[DOWN] = NO;
				_stack.push(_pMaze[x - 1][y]);
				continue;
			}

			_stack.pop();
		}
	}

	//打印迷宫路径搜索结果
	void showMazePath()
	{
		if (_stack.empty())
		{
			cout << "不存在一条迷宫路径!" << endl;
		}
		else
		{
			while (!_stack.empty())//栈不为空,取出节点坐标,相应值调整为* 
			{
				Node top = _stack.top();
				_pMaze[top._x][top._y]._val = '*';
				_stack.pop();
			}

			for (int i = 0; i < _row; ++i)//打印迷宫
			{
				for (int j = 0; j < _col; ++j)
				{
					if (_pMaze[i][j]._val == '*')
					{
						cout << "* ";
					}
					else
					{
						cout << _pMaze[i][j]._val << " ";
					}
				}
				cout << endl;
			}
		}
	}
private:
	//定义迷宫节点路径信息
	struct Node
	{
		int _x;//节点的横坐标 
		int _y;//节点的纵坐标 
		int _val;//节点的值
		int _state[WAY_NUM];//记录节点四个方向的状态(左右上下)
		//4个元素位置(左右上下),存储yes或者no 
	};

	Node **_pMaze;//动态生成迷宫路径(动态开辟二维数组) 
	int _row;//迷宫的行 
	int _col;//迷宫的列 
	stack<Node> _stack;//栈结构,辅助深度搜索迷宫路径
};

int main()
{
	cout << "请输入迷宫的行列数(例如:10 10):";
	int row, col, data;
	cin >> row >> col;

	Maze maze(row, col);//创建迷宫对象

	cout << "请输入迷宫的路径信息(0表示可以走,1表示不能走):" << endl;
	for (int i = 0; i < row; ++i)//只能获取i,j和data值,节点的4个方向的行走状态还不能初始化 
	{
		for (int j = 0; j < col; ++j)
		{
			cin >> data;
			//可以初始化迷宫节点的基本信息
			maze.initNode(i, j, data);
		}
	}

	//开始设置所有节点的四个方向的状态
	maze.setNodeState();

	//开始从左上角搜索迷宫的路径信息了
	maze.searchMazePath();

	//打印迷宫路径搜索的结果
	maze.showMazePath();

	return 0;
}

 

 2.广度优先遍历BFS

#include <iostream>
#include <queue>
#include <vector>
using namespace std;

//定义方向
const int RIGHT = 0;
const int DOWN = 1;
const int LEFT = 2;
const int UP = 3;
const int WAY_NUM = 4;

//定义行走状态
const int YES = 4;
const int NO = 5;

//迷宫
class Maze
{
public:
	Maze(int row, int col)
		:_row(row)
		, _col(col)
	{
		_pMaze = new Node*[_row];//开辟迷宫二维数组 
		for (int i = 0; i < _row; ++i)
		{
			_pMaze[i] = new Node[_col];
		}

		//node._x*_row + node._y
		_pPath.resize(_row * _col);//辅助数组开辟空间 
	}

	void initNode(int x, int y, int val)//初始化为No 
	{
		_pMaze[x][y]._x = x;
		_pMaze[x][y]._y = y;
		_pMaze[x][y]._val = val;
		for (int i = 0; i < WAY_NUM; ++i)
		{
			_pMaze[x][y]._state[i] = NO;
		}
	}

	void setNodeState()//设置迷宫行走状态 
	{
		for (int i = 0; i < _row; ++i)
		{
			for (int j = 0; j < _col; ++j)
			{
				if (_pMaze[i][j]._val == 1)
				{
					continue;
				}

				if (j < _col - 1 && _pMaze[i][j + 1]._val == 0)
				{
					_pMaze[i][j]._state[RIGHT] = YES;
				}

				if (i < _row - 1 && _pMaze[i + 1][j]._val == 0)
				{
					_pMaze[i][j]._state[DOWN] = YES;
				}

				if (j > 0 && _pMaze[i][j - 1]._val == 0)
				{
					_pMaze[i][j]._state[LEFT] = YES;
				}

				if (i > 0 && _pMaze[i - 1][j]._val == 0)
				{
					_pMaze[i][j]._state[UP] = YES;
				}
			}
		}
	}

	void searchMazePath()//广度优先遍历搜索 
	{
		if (_pMaze[0][0]._val == 1)
		{
			return;
		}
		_queue.push(_pMaze[0][0]);//入口节点入队 

		while (!_queue.empty())//队列不为空 
		{
			Node front = _queue.front();//获取队头元素 
			int x = front._x;
			int y = front._y;

			//右方向
			if (_pMaze[x][y]._state[RIGHT] == YES)
			{
				_pMaze[x][y]._state[RIGHT] = NO;
				_pMaze[x][y + 1]._state[LEFT] = NO;
				//在辅助数组中记录一下节点的行走信息
				_pPath[x*_row + y + 1] = _pMaze[x][y];
				_queue.push(_pMaze[x][y + 1]);
				if (check(_pMaze[x][y + 1]))
					return;
			}

			//下方向
			if (_pMaze[x][y]._state[DOWN] == YES)
			{
				_pMaze[x][y]._state[DOWN] = NO;
				_pMaze[x + 1][y]._state[UP] = NO;
				_pPath[(x + 1)*_row + y] = _pMaze[x][y];
				_queue.push(_pMaze[x + 1][y]);
				if (check(_pMaze[x + 1][y]))
					return;
			}

			//左方向
			if (_pMaze[x][y]._state[LEFT] == YES)
			{
				_pMaze[x][y]._state[LEFT] = NO;
				_pMaze[x][y - 1]._state[RIGHT] = NO;
				_pPath[x*_row + y - 1] = _pMaze[x][y];
				_queue.push(_pMaze[x][y - 1]);
				if (check(_pMaze[x][y - 1]))
					return;
			}

			//上方向
			if (_pMaze[x][y]._state[UP] == YES)
			{
				_pMaze[x][y]._state[UP] = NO;
				_pMaze[x - 1][y]._state[DOWN] = NO;
				_pPath[(x - 1)*_row + y] = _pMaze[x][y];
				_queue.push(_pMaze[x - 1][y]);
				if (check(_pMaze[x - 1][y]))
					return;
			}

			//当前节点出队列
			_queue.pop();
		}
	}

	void showMazePath()//打印迷宫 
	{
		if (_queue.empty())
		{
			cout << "不存在一条迷宫路径!" << endl;
		}
		else
		{
			//回溯寻找迷宫路径节点
			int x = _row - 1;
			int y = _col - 1;
			for (;;)
			{
				_pMaze[x][y]._val = '*';
				if (x == 0 && y == 0)
					break;
				Node node = _pPath[x*_row + y];
				x = node._x;
				y = node._y;
			}

			for (int i = 0; i < _row; ++i)
			{
				for (int j = 0; j < _col; ++j)
				{
					if (_pMaze[i][j]._val == '*')
					{
						cout << "* ";
					}
					else
					{
						cout << _pMaze[i][j]._val << " ";
					}
				}
				cout << endl;
			}
		}
	}
private:
	//定义迷宫节点路径信息
	struct Node
	{
		int _x;
		int _y;
		int _val;//节点的值
		int _state[WAY_NUM];//记录节点四个方向的状态
	};

	//检查是否是右下角的迷宫出口节点
	bool check(Node &node)
	{
		return node._x == _row - 1 && node._y == _col - 1;
	}

	Node **_pMaze;//动态开辟二维数组 
	int _row;//行 
	int _col;//列 
	queue<Node> _queue;//广度遍历依赖的队列结构
	vector<Node> _pPath;//记录广度优先遍历时,节点的行走信息,辅助数组 
};

int main()
{
	cout << "请输入迷宫的行列数(例如:10 10):";
	int row, col, data;
	cin >> row >> col;

	Maze maze(row, col);//创建迷宫对象

	cout << "请输入迷宫的路径信息(0表示可以走,1表示不能走):" << endl;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			cin >> data;
			//可以初始化迷宫节点的基本信息
			maze.initNode(i, j, data);
		}
	}

	//开始设置所有节点的四个方向的状态
	maze.setNodeState();

	//开始从左上角搜索迷宫的路径信息了
	maze.searchMazePath();

	//打印迷宫路径搜索的结果
	maze.showMazePath();

	return 0;
}

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

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

相关文章

Footprint Analytics 与 Core Chain 达成战略合作

​ 领先的区块链数据解决方案提供商 Footprint Analytics 与比特币驱动、EVM 兼容的 Layer 1 区块链 Core Chain 宣布达成战略合作。此次合作旨在将 Footprint Analytics 的前沿数据解决方案与 Core Chain 的区块链基础设施相结合&#xff0c;共同引领区块链领域的创新发展。 …

class常量池、运行时常量池和字符串常量池的关系

类常量池、运行时常量池和字符串常量池这三种常量池&#xff0c;在Java中扮演着不同但又相互关联的角色。理解它们之间的关系&#xff0c;有助于深入理解Java虚拟机&#xff08;JVM&#xff09;的内部工作机制&#xff0c;尤其是在类加载、内存分配和字符串处理方面。 类常量池…

【文献阅读】企业ESG表现与创新——来自A股上市公司的证据

企业ESG表现与创新——来自A股上市公司的证据 1.引言 第一段——背景介绍 可持续发展 碳达峰、碳中和 ESG既是从微观层面解决全球性社会问题的必要&#xff0c;也是实现我国经济转型、促进高质量发展的有效手段。 2017.12证监会&#xff1a;重点排污企业的环境披露 2021.6证监…

【C++】string类的使用④(字符串操作String operations || 常量成员Member constants)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; STL || C 目录 前言&#x1f525;字符串操作&#xff08;String operations&#xff09;c_strdataget_allocatorcopyfindrfindfind_first_offind_last_offind_first_not_offind_last_not…

编码器介绍与应用

一.概述 1.编码器 编码器&#xff0c;是一种用来测量机械旋转或位移的传感器。这种传感器能够测量机械部件在旋转或直线运动时的位移位置或速度等信息&#xff0c;并将其转换成一系列电信号。其可和电机组装到一起用&#xff0c;反馈电机方向、转换角度的&#xff0c;然后电机…

MongoDB和AI 赋能行业应用:制造业和汽车行业

欢迎阅读“MongoDB和AI 赋能行业应用”系列的第一篇。 本系列重点介绍AI应用于不同行业的关键用例&#xff0c;涵盖制造业和汽车行业、金融服务、零售、电信和媒体、保险以及医疗保健行业。 随着人工智能&#xff08;AI&#xff09;在制造业和汽车行业的集成&#xff0c;传统…

求四个整数中的最大值(函数)(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int a, b, c, d, max;//获取用户输入的数据&#xff1b;printf("请输入4个整数&#x…

内部开发平台如何赋能开发人员与业务

一个厨师只有具备烹饪美食的技能与经验&#xff0c;并且在设备、工具齐全的餐厅里才能发挥他的才能。交响乐团需要正确的乐器、指挥家和舞台才能演奏初美妙的音乐。 而在软件开发的世界&#xff0c;开发人员需要最好的工具包和开发环境来设计开发他们的软件项目。这个环境就被…

Mysql数据类型设计思考

一、Mysql数据类型设计规范 1.1 选择更小的数据类型 一般情况下&#xff0c;在满足存储要求的基础上&#xff0c;尽量选择小的存储类型。例如&#xff1a;存储0~200&#xff0c;tinyint和bigint都可以存储&#xff0c;那么选择tinyint。原因&#xff1a;越小的数据类型运算速…

计算机组成原理(超详解!!) 第七节 中央处理器(下)

1.微程序控制器 微程序设计技术&#xff1a;利用软件方法来设计硬件的一门技术。 微程序控制器的基本思想&#xff1a; 仿照通常的解题程序的方法&#xff0c;把操作控制信号编成所谓的“微指令”&#xff0c;存放到一个只读存储器里。当机器运行时&#xff0c;一条又一条地…

高速电流反馈运放总结

目录 前言 基础架构 CFB运算放大器拓扑结构的进步 前言 最近项目发现有震荡&#xff0c;发现是电流反馈型运放导致&#xff0c;所以对电流运放的知识做了全面的复习。 基础架构 现在&#xff0c;我们将详细考察高速运算放大器中非常流行的电流反馈(CFB)运算放大器拓扑结 构…

Vue 局部布局 Layout 内部布局 [el-row]、[el-col]

之前的布局容器是一个整体的框架&#xff0c;layout里面的布局其实就是el-row和el-col的组合。 基础布局 使用单一分栏创建基础的栅格布局。 通过 ​row ​和 ​col ​组件&#xff0c;并通过 ​col ​组件的 ​span ​属性我们就可以自由地组合布局。 这种最简单&#xff0c;…

网络库-libevent介绍

1.简介 libevent是一个事件驱动的网络库&#xff0c;主要用于构建可扩展的网络服务器。它提供了跨平台的API&#xff0c;支持多种事件通知机制&#xff0c;如select、poll、epoll、kqueue等。 主要组件 event: 表示一个具体的事件&#xff0c;包括事件类型、事件回调等。eve…

Office之Word应用(二)

一、页眉添加文件名称和页码 1、双击页眉&#xff0c;点击“页眉-空白&#xff08;三栏&#xff09;” 2、删掉第一处&#xff08;鼠标放在上面就会选中&#xff0c;Enter即可&#xff09;&#xff0c;第二处输入文档名称&#xff0c;第三处插入页码。 注&#xff1a;插入页码时…

【CSP CCF记录】202203-2 出行计划

题目 过程 第一次提交 暴力求解&#xff0c;时间复杂度为n*n&#xff0c;超时 #include<bits/stdc.h> using namespace std; const int N100001; int n,m,k; int t[N],c[N],q[N]; int main() {cin>>n>>m>>k;for(int i0;i<n;i){cin>>t[i]&g…

计算机视觉的应用30-基于深度卷积神经网络CNN模型实现物体表面缺陷检测技术的项目

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用30-基于深度卷积神经网络CNN模型实现物体表面缺陷检测技术的项目主要包括&#xff1a;物体表面缺陷检测技术项目介绍&#xff0c;数据构造&#xff0c;模型介绍。 物体表面缺陷检测技术是工业自动化…

深入探讨黑盒测试:等价类划分与边界值分析

文章目录 概要黑盒测试等价类划分边界值分析 设计测试用例小结 概要 在软件开发领域&#xff0c;测试是确保产品质量的关键步骤之一。而黑盒测试方法作为其中的一种&#xff0c;通过关注输入与输出之间的关系&#xff0c;而不考虑内部实现的细节&#xff0c;被广泛应用于各种软…

最短木板长度 - 贪心思维

系列文章目录 文章目录 系列文章目录前言一、题目描述二、输入描述三、输出描述四、java代码五、测试用例 前言 本人最近再练习算法&#xff0c;所以会发布自己的解题思路&#xff0c;希望大家多指教 一、题目描述 小明有 n 块木板&#xff0c;第 i ( 1 ≤ i ≤ n ) 块木板长…

3分钟,学会一个 Lambda 小知识之【流API】

之前给大家介绍的 Lambda 小知识还记得吗&#xff1f;今天再来给大家介绍&#xff0c; 流API 的相关知识要点。 流API Stream是Java8中处理集合的关键抽象概念&#xff0c;它可以指定你对集合的&#xff0c;可以执行查找、过滤和映射等数据操作。 Stream 使用一种类似用 SQ…

SSRF(服务器端请求伪造)的学习以及相关例题(上)

目录 一、SSRF的介绍 二、漏洞产生的原因 三、利用SSRF可以实现的效果&#xff08;攻击方式&#xff09; 四、SSRF的利用 五、SSRF中的函数 file_get_content() 、fsockopen() 、curl_exec() 1.file_get_content()&#xff1a; 2.fsockopen(): 3.curl_exec()&#xff1…