数据结构与算法 迷宫问题
迷宫问题问题描述:
栈的实现在这篇博客中:https://blog.csdn.net/zj1131190425/article/details/87991662
迷宫是一个矩形区域,有一个出口和入口,迷宫内部包含不能穿越的墙壁和障碍物。
1.用矩阵来描述迷宫。入口是(0, 0), 出口是(m,n),现在需要在迷宫中选找一条路径,穿过迷宫。位置(i,j)=1表示有障碍。0表示无障碍。
思路:
1.出发点处,定义四个方向的坐标偏移量,搜索等四个方向,直到找到一个可行的方向,按照可行的方向移动到下一个位置,将前一个位置坐标压入栈中(保存路径),且将此位置设置为1(放置障碍物,防止往回退的时候又重新搜索这个方向)
2. 如果在某个位置没有找到可行的方向,则将当前这个位置设置为1(这个位置不可再回来),从栈顶弹出一个坐标,作为当前坐标(相当于走回头路),继续搜索其他的方向是否可行。
3. 如此往复
采用自顶向下设计的方法设计代码:
(1)输入迷宫
(2)寻找路径
(3)输出路径
#include <iostream> #include "E:\back_up\code\c_plus_code\sparseMatrix\external_file\arraystack.h" #include <string> using namespace std; // 迷宫 template<typename T> void print_array(T a[][12]) { for(int i=0; i<12; i++) { for(int j=0; j<12; j++) { cout << a[i][j] << " "; } cout << endl; } cout << endl; } class position // 存储位置坐标 { public: int row; int col; position() { row = 0; col = 0; } position(int row, int col) { this->row = row; this->col = col; } void add_offset(position& ps) { row += ps.row; col += ps.col; } }; void find_path(bool map[][12]) { int map_1[12][12]; // 保存map()的一个副本 for(int i=0; i<12; i++) { for(int j=0; j<12; j++) { map_1[i][j] = (int)map[i][j]; } } // 寻找迷宫的路径 arrayStack<position> path; // 存储路径信息 // 偏移量 position offset[4]; // 向右偏移 index=0 offset[0].row = 0; offset[0].col = 1; // 向下偏移 index=1 offset[1].row = 1; offset[1].col = 0; // 向左偏移 index=2 offset[2].row = 0; offset[2].col = -1; // 向上偏移 index=3 offset[3].row = -1; offset[3].col = 0; position current_position(1,1); // 初始化当前位置 int option = 0; //初始化选的方向,右,下,左,上=(0,1,2,3) int max_otion = 3; // 方向的选择范围 map[1][1] = 1; // 在(1,1)位置放置障碍 cout << "current position: " << current_position.row << " " << current_position.col << endl; while(current_position.row!=10 || current_position.col!=10) { int r,c; while(option<=max_otion) { r = current_position.row + offset[option].row; c = current_position.col + offset[option].col; if(map[r][c]==0) // 新的位置可行 { break; } option++; } if(option<=max_otion) // 找到了可行的方向 { path.push(current_position); //current_position.add_offset(offset[position]); // current_position更新 current_position.row = r; // 移动到新的位置 current_position.col = c; map[r][c] = 1; // 放置障碍物 避免回退时又选到这个方向 option = 0; } else // 选择的位置走不通 { if(path.empty()) { cout << "The puzzle has no solution" << endl; return; } position next = path.top(); path.pop(); // 计算返回后的搜索方向: // 按照前面对方向的定义,返回后应该搜索未搜索过的两个方向: // map[currentRow][currentCol] = 1; // 走不通的位置设置障碍,避免重复搜索 map[current_position.row][current_position.col] = 1; // 当前的位置是死路 option = 0; current_position = next; // 浅复制即可 这个类默认的拷贝构造函数 } cout << "(r,c)= " << r << "," << c << endl; } position tmp; while(!path.empty()) { // cout << "revise the map" << endl; tmp = path.top(); path.pop(); map_1[tmp.row][tmp.col] = 2; } // 结果输出 char result[12][12]; for(int i=0; i<12; i++) { for(int j=0; j<12; j++) { if(map_1[i][j]==1) { result[i][j]='1'; } else if(map_1[i][j]==0) { result[i][j]='0'; } else { result[i][j]='*'; } } } print_array(result); } int main(int argc, char *argv[]) { //int a[12][12]; bool a[][12] = { {1,1,1,1,1,1,1,1,1,1,1,1}, {1,0,1,1,1,1,1,0,0,0,0,1}, {1,0,0,0,0,0,1,0,1,0,0,1}, {1,0,0,0,1,0,1,0,0,0,0,1}, {1,0,1,0,1,0,1,0,1,1,0,1}, {1,0,1,0,1,0,1,0,1,0,0,1}, {1,0,1,1,1,0,1,0,1,0,1,1}, {1,0,1,0,0,0,1,0,1,0,1,1}, {1,0,1,0,1,1,1,0,1,0,0,1}, {1,1,0,0,0,0,0,0,1,0,0,1}, {1,0,0,0,0,1,1,1,1,0,0,1}, {1,1,1,1,1,1,1,1,1,1,1,1} }; //print_array(a); find_path(a); return 0; }
运行结果:
迷宫问题最短路径
可以使用队列来实现寻找最短路径的操作
首先介绍C++ 实现队列:关于队列的实现参考博客: https://blog.csdn.net/zj1131190425/article/details/88090905,下面的队列是对https://blog.csdn.net/zj1131190425/article/details/88090905中队列的改进,主要是改经了ensureCapacity()函数,因为原来的队列中ensureCapacity()函数有点小的bug.
定义一个队列的基类,queueABC
queueABC.h文件
#ifndef QUEUE_ABC_H #define QUEUE_ABC_H using namespace std; // 定义抽象类 template<typename T> class queueABC { public: // virtual ~queue(); virtual bool empty() const=0; // 纯虚函数 只读 virtual int size() const=0; // 返回队列中元素的个数 virtual T& front() = 0; // 返回队首元素 virtual T& back() = 0; // 返回队尾元素 virtual void pop() = 0; // 删除队首的元素 virtual void push(T x) = 0; // 队尾插入元素 }; #endif
queue.h
#ifndef QUEUE_H #define QUEUE_H #include <iostream> #include <algorithm> #include "E:\back_up\code\c_plus_code\dequeue\external_file\queueABC.h" // 包含ABC文件 #include "E:\back_up\code\c_plus_code\dequeue\external_file\queueemptyEx.h" // 包含异常类文件 using namespace std; template<typename T> class queue : public queueABC<T> { private: int arrayLength; // 数组的长度 int queueSize; // 队列中元素的个数 int queueFront; // 队首元素所在的位置 int queueBack; // 队尾元素所在的位置 T* element; void ensureArrayLength(); // 进行数组扩容 public: queue(int arrayLength=10); // 构造函数 ~queue(); // 析构函数 queue(const queue& q); // 拷贝构造函数 // ADT bool empty() const; int size() const; T& front(); T& back(); void pop(); void push(T x); void display_queue() const; // 打印输出队列中的元素 }; template<typename T> queue<T>::queue(int arrayLength) { this->arrayLength = arrayLength; this->queueSize = 0; this->queueFront = 0; this->queueBack = 0; element = new T[arrayLength]; } template<typename T> queue<T>::~queue() { delete [] element; } template<typename T> queue<T>::queue(const queue& q) { arrayLength = q.arrayLength; queueSize = q.queueSize; queueFront = q.queueFront; queueBack = q.queueBack; element = new T[arrayLength]; for(int i=0; i<queueSize; i++) { element[i] = q.element[i]; } } template<typename T> bool queue<T>::empty() const { return queueSize==0; } template<typename T> int queue<T>::size() const { return queueSize; } template<typename T> T& queue<T>::front() { return element[(queueFront+1)%arrayLength]; // queueFront是队首元素的前一个位置,+1是表示队首元素的位置 } template<typename T> T& queue<T>::back() { return element[queueBack%arrayLength]; // queueback队尾元素的位置 } /* template<typename T> void queue<T>::ensureArrayLength() { // 如果需要,增加数组长度 if((queueBack+1)%arrayLength==queueFront) // 环形的数组 { T* old; old = element; //delete element; // arrayLength = arrayLength*2; // 增加分配的内存长度 element = new T[2*arrayLength]; // 环形数组重新布局 // 先将数组复制过来 //更新关于数组复制 int index_tmp; for(int i=0; i<queueSize; i++) { index_tmp = (queueFront+1+i)%arrayLength; element[index_tmp] = old[index_tmp]; } delete old; // 重新布局 // 分为三种情况:1.queuefront=0; queuefront==arrayLength-1; else; int pre_start = queueFront%arrayLength; // 队列首元素的前一个位置 if(pre_start==0) { // 移动所有的数组元素 for(int i=0; i<queueSize; i++) { element[2*arrayLength-1-i] = element[(queueBack%arrayLength)-i]; } queueFront = queueFront + arrayLength; queueBack = queueBack + arrayLength; } else if(pre_start==arrayLength-1) // arrayLength还是原来的值 { // 不用移动数组元素,之更改front queueFront = queueFront + arrayLength; } else { // 移动第二段的元素: int element_to_move = arrayLength-1-(queueFront%arrayLength); // 计算第二段需要移动的元素的数量 for(int i=0; i<element_to_move; i++) { element[2*arrayLength-1-i] = element[queueBack%arrayLength-i]; } queueFront = queueFront + arrayLength; } arrayLength = arrayLength*2; // 更新arrayLength } // 添加一个动态减少内存的函数 } */ template<typename T> void queue<T>::ensureArrayLength() { if((queueBack+1)%arrayLength==queueFront) // 环形的数组 { T* new_element = new T[2*arrayLength]; int start = (queueFront+1)%arrayLength; if(start<2) // 没有形成环 { copy(element+start, element+start+arrayLength-1, new_element); // 复制操作 } else // 形成环 { copy(element+start, element+arrayLength, new_element); copy(element, element+queueBack+1, new_element+arrayLength-start); } queueFront = 2*arrayLength-1; queueBack = arrayLength-2; arrayLength *= 2; delete[] element; element = new_element; } } // push()操作: template<typename T> void queue<T>::push(T x) { ensureArrayLength(); // 先保证数组长度满足条件 queueSize++; //queueBack = (queueBack+1)%arrayLength; //element[queueBack] = x; queueBack++; element[queueBack%arrayLength] = x; } //pop()操作 template<typename T> void queue<T>::pop() { if(empty()) throw queueEmptyException(0); queueFront++; queueSize--; // element[queueFront].~T; } template<typename T> void queue<T>::display_queue() const { //int tmp = queueFront; // int start_pos = (tmp+1)%arrayLength; if(empty()) { cout << "The queue is empty!" << endl; return; } for(int i=0; i<queueSize; i++) { cout << element[(queueFront+1+i)%arrayLength] << " "; } cout << endl; } #endif
自定义的异常类:
queueemptyException:
// 自定义异常类 #ifndef QUEUE_EMPTY_EXCEPTION #define QUEUE_EMPTY_EXCEPTION #include <stdexcept> #include <iostream> using namespace std; class queueEmptyException : public runtime_error { private: int queueSize; public: queueEmptyException(int queueSize) : runtime_error("The queue empty!") { this->queueSize = queueSize; } void display_error_info() { cout << "The queue size is " << queueSize << endl; } }; #endif
寻找最短路径的算法思路:
从上面的迷宫中,找一条从入口到出口的最短路径,要有两个过程:
1. 一个是距离标记过程
2. 另一个是路径标记过程
在距离标记过程中,把能从入口到达的相邻位置标记为1,然后把从编号为1可到达的相邻位置标记为2(表示与入口相距为2),直到到达出口或者没有可标记的相邻位置为止。
在路径标记过程中,从出口开始,首先移动到比出口的编号小1的位置,再从当前位置移动到比其编号小1的位置上,直到到达起点为止,这便是最短的路径
3.按照上述思路编写代码实现:
main.cpp
#include <iostream> #include "E:\back_up\code\c_plus_code\dequeue\external_file\queue.h" using namespace std; // 迷宫 template<typename T> void print_array(T **a, int size) { for(int i=0; i<size; i++) { for(int j=0; j<size; j++) { cout << a[i][j] << " "; } cout << endl; } cout << endl; } class position // 存储位置坐标 { public: int row; int col; position() { row = 0; col = 0; } position(int row, int col) { this->row = row; this->col = col; } /* void add_offset(position& ps) { row += ps.row; col += ps.col; } */ }; void findPath(int** map, int mapSize=10) { // 这里默认起始点为(0,0),终点为(9,9) // 给地图添加围墙: int** new_map = new int*[mapSize+2]; for(int i=0; i<mapSize+2; i++) { new_map[i] = new int[mapSize+2]; } for(int i=0; i<mapSize+2; i++) { for(int j=0; j<mapSize+2; j++) { if(i==0 || i==mapSize+2-1) { new_map[i][j] = 1; // 增加行围墙 } else if(j==0 || j==mapSize+2-1) //增加列围墙 { new_map[i][j] = 1; } else // 保存地图 { new_map[i][j] = map[i-1][j-1]; } } } // print_array(map, mapSize); cout << "原始地图: " << endl; print_array(new_map, mapSize+2); // 初始化偏移量 position offset[4]; // 向右偏移 index=0 offset[0].row = 0; offset[0].col = 1; // 向下偏移 index=1 offset[1].row = 1; offset[1].col = 0; // 向左偏移 index=2 offset[2].row = 0; offset[2].col = -1; // 向上偏移 index=3 offset[3].row = -1; offset[3].col = 0; position current_position(1,1); // 初始化当前位置 position end_position(10,10); new_map[current_position.row][current_position.col] = 2; // 这里距离标记从2开始,实际距离等于标号-2 int direction_number = 4; // 一个方格相邻位置的个数 position neighbor; queue<position> q; // 队列,用于存放标记的位置 while(true) { // 给相邻位置做标记 for(int i=0; i<direction_number; i++) { neighbor.row = current_position.row + offset[i].row; neighbor.col = current_position.col + offset[i].col; if(new_map[neighbor.row][neighbor.col]==0) // 可标记的位置 { new_map[neighbor.row][neighbor.col] = new_map[current_position.row][current_position.col]+1; // 标记 if(neighbor.row==end_position.row && neighbor.col==end_position.col) { break; // 标记完成 } q.push(neighbor); } } if(neighbor.row==end_position.row && neighbor.col==end_position.col) { break; // 到达 } if(q.empty()) // 迷宫无解; { cout << "The puzzle has no solution" << endl; return; } current_position = q.front(); q.pop(); } //cout << "标记完的矩阵: " << endl; //print_array(new_map, 12); // 构造路径; int pathLength = new_map[end_position.row][end_position.col]-2; // 路径的长短 // position* path = new position[pathLength]; queue<position> q_path; // 存储路径 current_position = end_position; // 从终点回溯 for(int i=pathLength-1; i>=0; i--) { // path[i] = current_position; q_path.push(current_position); // 当前的位置放入队列中 for(int j=0; j<direction_number; j++) { neighbor.row = current_position.row + offset[j].row; neighbor.col = current_position.col + offset[j].col; if(new_map[neighbor.row][neighbor.col] == i+2) { break; } } //测试 // cout << "current position: " << current_position.row << ',' << current_position.col << endl; current_position = neighbor; } /* 测试代码 position tmp; while(!q_path.empty()) { tmp = q_path.front(); cout << "path: " << tmp.row << "," << tmp.col << endl; q_path.pop(); } */ /* 测试代码 for(int i=0; i<pathLength; i++) { cout << "path: " << path[i].row << "," << path[i].row << endl; } */ // 输出结果 char** result = new char*[mapSize+2]; // 创建一个新的矩阵保存可视化结果 for(int i=0; i<mapSize+2; i++) { result[i] = new char[mapSize+2]; } // 初始化: position tmp; while(!q_path.empty()) { tmp = q_path.front(); result[tmp.row][tmp.col] = '*'; q_path.pop(); } for(int i=0; i<mapSize+2; i++) { for(int j=0; j<mapSize+2; j++) { if(result[i][j] != '*' && new_map[i][j]==1) { result[i][j] = '1'; } if(result[i][j] != '*' && (new_map[i][j]==0 || new_map[i][j]>1)) { result[i][j] = '0'; } } } // 输出迷宫路径 cout << "最短路径: " << pathLength << endl; cout << "迷宫路径如下所示: " << endl; print_array(result, mapSize+2); } int main(int argc, char *argv[]) { int map_size = 10; int input_map[10][10] = { {0,1,1,1,1,1,0,0,0,0}, {0,0,0,0,0,1,0,1,0,0}, {0,0,0,1,0,1,0,0,0,0}, {0,1,0,1,0,1,0,1,1,0}, {0,1,0,1,0,1,0,0,0,0}, {0,1,0,0,0,1,0,1,0,1}, {0,1,0,0,0,1,0,1,0,1}, {1,0,0,0,0,0,0,1,0,0}, {0,0,0,0,1,1,1,1,0,0} }; // 数据转换 int** map_in = new int*[map_size]; for(int i=0; i<map_size; i++) { map_in[i] = new int[map_size]; } for(int i=0; i<map_size; i++) { for(int j=0; j<map_size; j++) { map_in[i][j] = input_map[i][j]; } } // 将原始地图转换为指针的形式 findPath(map_in, map_size); return 0; }
算法的运行结果如下图所示:
---------------------------------------------------end----------------------------------------------------------
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)