迷宫路径&广度优先求解&深度优先求解&迷宫游戏
2016-09-13 18:57
417 查看
求解迷宫的通道是数据结构中一个很经典的问题,解决这个问题大概有两种思路,一
种是深度优先,一种是广度优先。深度优先是只要找到一条通道就可以了,而广度优先
则是寻找最短的路径。下面我们来对这两种思想做一个剖析。
深度优先:
如果要用一句俗语来解释深度优先的话,那么一定是“不撞南墙不回头”。深度优先的重点于“深”,找到一条路就一头扎下去,直到走不通才回头。由于这里有了回退的要求,所以就要借用栈来实现。
用深度优先来求解迷宫路径的算法:
广度优先:
如果要用一个成语来描述广度优先的话,那么这个成语就是“层层递进”。广度优先的重点再于“广”,从一点开始辐射状的遍历周围所有可能到达的点。广度优先通常都要借助队列来实现。
广度优先求解迷宫最短路径算法:
下面为大家在介绍一下简单迷宫游戏的简单实现:
要实现迷宫游戏需要具备一下条件:
1、地图
可以开辟一个二维数组,然后用随机数生成地图,为了保证地图的起点和终点不被用,
所以在随机生成的地图后再将起点和终点单独拿出来设置。
2、控制键
通过方向键来获得小人下一步要走的位置,然后拿到这个位置,判断是否越界和是
否能够通过。获得位置的函数如下:
//分别按上下左右时,a2的值分别是72 80 75 77 a1的值都是224
#include<conio.h>
int a1 = _getch();
int a2 = _getch();
3、重绘
为了使得小人能够动起来,我们在得到小人下一步的坐标时,就要先将当前位置的
小人先擦除掉,然后再在下个坐标将小人重新绘制出。
获得光标位置的函数如下:
//调用move函数,给move传一个坐标,则光标就跳转到坐标处
//对于光标来说,他在显示屏上的坐标是,以左上角为原点,横坐标为x,向右增长
//纵坐标为y向下增长
#include<Windows.h>
void move(int x, int y)
{
COORD cd;
cd.X = x;
cd.Y = y;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(handle, cd);
}
种是深度优先,一种是广度优先。深度优先是只要找到一条通道就可以了,而广度优先
则是寻找最短的路径。下面我们来对这两种思想做一个剖析。
深度优先:
如果要用一句俗语来解释深度优先的话,那么一定是“不撞南墙不回头”。深度优先的重点于“深”,找到一条路就一头扎下去,直到走不通才回头。由于这里有了回退的要求,所以就要借用栈来实现。
用深度优先来求解迷宫路径的算法:
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cassert> using namespace std; typedef struct NodePos { int x; int y; NodePos(int row=0, int col=0) :x(row) , y(col){} }Node; //因为迷宫用的栈 是我自己的,为了方便验证,我将栈也给出 template<typename T> class Stack { public: Stack() :_capacity(0) , _size(0) , _ptr(NULL) {} ~Stack() { delete[] _ptr; _size = 0; _capacity = 0; _ptr = NULL; } void Push(const T& data); void Pop(); T Top(); int Size(); bool Empty(); private: void CheckCapacity() { if (_size >= _capacity) { int NewCapacity = _capacity * 2 + 1; T* tmp = new T[NewCapacity]; for (int i = 0; i < _size; i++) { tmp[i] = _ptr[i]; } delete[] _ptr; _ptr = tmp; _capacity = NewCapacity; } } private: T* _ptr; int _capacity; int _size; }; template<typename T> bool Stack<T>::Empty() { if (_size == 0) return true; else return false; } template<typename T> void Stack<T>::Push(const T& data) { CheckCapacity(); _ptr[_size++] = data; } template<typename T> void Stack<T>::Pop() { assert(_size>0); --_size; } template<typename T> T Stack<T>::Top() { return _ptr[_size - 1]; } template<typename T> int Stack<T>::Size() { return _size; } //下面用到的栈是自己实现的栈 template<typename T> class Maze { typedef Node Pos; public: Maze() :_row(0) , _col(0) , _start(0,0) , _map(NULL) {} ~Maze() { for (int i = 0; i < _row; i++) { delete[] _map[i]; } delete[] _map; } bool SearchPath(); //查找迷宫路径 void PrintMap(); //输出迷宫地图 void PrintPath(); //打印路径的坐标 void SetMap(); //设置地图 private: bool CheckNextAccess(Pos coor); //判断该坐标能否通过 private: int _row; int _col; T **_map; Stack<Pos> _s; Pos _start; }; template<typename T> bool Maze<T>::CheckNextAccess(Pos coor) //判断该坐标能否通过 { if (coor.x >= 0 && coor.x < _row &&coor.y >= 0 && coor.y < _col &&_map[coor.x][coor.y] == 0) //判断是否越界 { return true; } else { return false; } } template<typename T> void Maze<T>::SetMap() { char c; FILE *fp = fopen("MazeMap.txt", "r"); //打开文件读取地图 assert(fp); //读取行 while ((c = getc(fp))!= ' '&&c != '\n') { _row=_row*10+(c-'0'); } //读取迷宫列 while ((c = getc(fp)) != ' '&&c != '\n') { _col=_col*10+(c - '0'); } //读取迷宫入口横坐标 while ((c = getc(fp)) != ' '&&c != '\n') { _start.x = _start.x*10+(c - '0'); } //读取迷宫入口纵坐标 while ((c = getc(fp)) != ' '&&c != '\n') { _start.y = _start.y*10+(c - '0'); } //开辟迷宫数组 _map = new T*[_row]; for (int i = 0; i < _row; i++) { _map[i] = new T[_col]; } //读取迷宫地图 for (int i = 0; i < _row; i++) { for (int j = 0; j < _col;) { c = getc(fp); if (c == '1' || c == '0') { _map[i][j] = c - '0'; j++; } } } fclose(fp); //关闭文件 } template<typename T> bool Maze<T>::SearchPath() { Pos cur = _start; _s.Push(_start); //将起点坐标压入栈 _map[_start.x][_start.y] = 2; //走过的路径设置为2 while (!_s.Empty()) //如果栈以空,则说明又回到入口,则迷宫无解 { Pos next=_s.Top(); cur = next; _map[cur.x][cur.y] = 2; //将坐标设置为已走过 if (next.x==_row-1||next.y==_col-1) //判断是否走到出口 { return true; } //上 next.x--; if (CheckNextAccess(next)) //如果当前坐标能够通过,则加入坐标 { _s.Push(next); continue; } next.x++; //下 next.x++; if (CheckNextAccess(next)) { _s.Push(next); continue; } next.x--; //左 next.y--; if (CheckNextAccess(next)) { _s.Push(next); continue; } next.y++; //右 next.y++; if (CheckNextAccess(next)) { _s.Push(next); continue; } _s.Pop(); //如果当前坐标不通,则退栈 _map[next.x][next.y]= 3; //并将这个坐标标记为不通 } return false; } template<typename T> void Maze<T>::PrintMap() //打印迷宫地图,但是必须先设置迷宫地图 { for (int i = 0; i < _row; i++) { for (int j = 0; j < _col; j++) { cout << _map[i][j] <<" "; } cout << endl; } } template<typename T> void Maze<T>::PrintPath() //打印迷宫路径 { Stack<Pos> coor; while (!_s.Empty()) { coor.Push(_s.Top()); _s.Pop(); } while (!coor.Empty()) { cout << "(" << coor.Top().x << "," << coor.Top().y << ")" << endl; coor.Pop(); } } void test() { Maze<int> m; m.SetMap(); //先设置地图 m.PrintMap(); cout << (m.SearchPath() == 1 ? "Yes":"NO") << endl; m.PrintMap(); m.PrintPath(); } int main() { test(); system("pause"); return 0; }
广度优先:
如果要用一个成语来描述广度优先的话,那么这个成语就是“层层递进”。广度优先的重点再于“广”,从一点开始辐射状的遍历周围所有可能到达的点。广度优先通常都要借助队列来实现。
广度优先求解迷宫最短路径算法:
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cassert> #include<queue> using namespace std; typedef struct NodePos { int x; int y; NodePos(int row = 0, int col = 0) :x(row) , y(col){} }Node; //因为迷宫用的栈 是我自己的,为了方便验证,我将栈也给出 template<typename T> class Stack { public: Stack() :_capacity(0) , _size(0) , _ptr(NULL) {} ~Stack() { delete[] _ptr; _size = 0; _capacity = 0; _ptr = NULL; } void Push(const T& data); void Pop(); T Top(); int Size(); bool Empty(); private: void CheckCapacity() { if (_size >= _capacity) { int NewCapacity = _capacity * 2 + 1; T* tmp = new T[NewCapacity]; for (int i = 0; i < _size; i++) { tmp[i] = _ptr[i]; } delete[] _ptr; _ptr = tmp; _capacity = NewCapacity; } } private: T* _ptr; int _capacity; int _size; }; template<typename T> bool Stack<T>::Empty() { if (_size == 0) return true; else return false; } template<typename T> void Stack<T>::Push(const T& data) { CheckCapacity(); _ptr[_size++] = data; } template<typename T> void Stack<T>::Pop() { assert(_size>0); --_size; } template<typename T> T Stack<T>::Top() { return _ptr[_size - 1]; } template<typename T> int Stack<T>::Size() { return _size; } //下面用到的栈是自己实现的栈 template<typename T> class Maze { typedef Node Pos; public: Maze() :_row(0) , _col(0) , _start(0, 0) , _map(NULL) ,_book(NULL) {} ~Maze() { for (int i = 0; i < _row; i++) { delete[] _map[i]; delete[] _book[i]; } delete[] _map; delete[] _book; } bool SearchPath(); //查找迷宫路径 void PrintMap(); //输出迷宫地图 void PrintPath(); //打印路径的坐标 void SetMap(); //设置地图 private: bool CheckNextAccess(Pos coor); //判断下一个坐标能否通过 bool CheckPrevAccess(Pos coor); //判断前一个坐标能否通过 private: int _row; int _col; T **_map; //地图数组 T **_book; //辅助数组 Stack<Pos> _s; Pos _start; }; template<typename T> bool Maze<T>::CheckNextAccess(Pos coor) //判断该坐标能否通过 { if (coor.x >= 0 && coor.x < _row &&coor.y >= 0 && coor.y < _col &&_map[coor.x][coor.y] == 0) //判断是否越界 { return true; } else { return false; } } template<typename T> bool Maze<T>::CheckPrevAccess(Pos coor) //判断该坐标能否通过 { if (coor.x >= 0 && coor.x < _row &&coor.y >= 0 && coor.y < _col &&(_book[coor.x][coor.y] == (_book[_s.Top().x][_s.Top().y] - 1))) //判断是否越界 { return true; } else { return false; } } template<typename T> bool Maze<T>::SearchPath() { queue<Node> q; q.push(_start); //起点入队 Pos cur = _start; int step = 1; _book[cur.x][cur.y] = 0; //将起点标记为0 while (!q.empty()) //如果队列不空,广度优先查找 { Pos next = q.front(); cur = next; _map[cur.x][cur.y] = 2; //将走过的路径标记位2 step = _book[q.front().x][q.front().y] + 1; //在辅助数组中标记处当前结点的层数 if (((cur.x == _row - 1)&&cur.x!=_row-1) || ((cur.y == _col- 1)&&cur.y != _col- 1) || (cur.x == 0 && _start.x != 0) || (cur.y == 0 && _start.y != 0)) //如果找到出口则跳出循环,出口与入口不再同一侧 { break; } //上 next.x--; if (CheckNextAccess(next)) //如果当前坐标能够通过,则加入坐标 { q.push(next); _book[next.x][next.y] = step; } next.x++; //下 next.x++; if (CheckNextAccess(next)) { q.push(next); _book[next.x][next.y] = step; } next.x--; //左 next.y--; if (CheckNextAccess(next)) { q.push(next); _book[next.x][next.y] = step; } next.y++; //右 next.y++; if (CheckNextAccess(next)) { q.push(next); _book[next.x][next.y] = step; } next.y--; q.pop(); //如果都不能通,则将这点出队 } if (!q.empty()) //如果队列不空,则将终点压入栈中 { _s.Push(cur); } //倒着寻找路径 while (!_s.Empty()) //倒着将从终点到起点的坐标压入 { Pos next = _s.Top(); //上 if (_book[next.x][next.y] == 0) //如果找到起点 { return true; } next.x--; if (CheckPrevAccess(next)) //如果当前坐标能够通过,则加入坐标 { _s.Push(next); continue; } next.x++; //下 next.x++; if (CheckPrevAccess(next)) { _s.Push(next); continue; } next.x--; //左 next.y--; if (CheckPrevAccess(next)) { _s.Push(next); continue; } next.y++; //右 next.y++; if (CheckPrevAccess(next)) { _s.Push(next); continue; } next.y--; } return false; } template<typename T> void Maze<T>::SetMap() { char c; FILE *fp = fopen("MazeMap.txt", "r"); //打开文件读取地图 assert(fp); //读取行 while ((c = getc(fp)) != ' '&&c != '\n') { _row = _row * 10 + (c - '0'); } //读取迷宫列 while ((c = getc(fp)) != ' '&&c != '\n') { _col = _col * 10 + (c - '0'); } //读取迷宫入口横坐标 while ((c = getc(fp)) != ' '&&c != '\n') { _start.x = _start.x * 10 + (c - '0'); } //读取迷宫入口纵坐标 while ((c = getc(fp)) != ' '&&c != '\n') { _start.y = _start.y * 10 + (c - '0'); } //开辟迷宫数组 _map = new T*[_row]; for (int i = 0; i < _row; i++) { _map[i] = new T[_col]; } //开辟辅助数组 _book = new T*[_row]; for (int i = 0; i < _row; i++) { _book[i] = new T[_col]; } //初始化辅助数组 for (int i = 0; i < _row; i++) { for (int j = 0; j < _col; j++) { _book[i][j] = -1; } } //读取迷宫地图 for (int i = 0; i < _row; i++) { for (int j = 0; j < _col;) { c = getc(fp); if (c == '1' || c == '0') { _map[i][j] = c - '0'; j++; } } } fclose(fp); //关闭文件 } template<typename T> void Maze<T>::PrintMap() //打印迷宫地图,但是必须先设置迷宫地图 { cout << "迷宫地图中所走过的路径" << endl; for (int i = 0; i < _row; i++) { for (int j = 0; j < _col; j++) { cout << _map[i][j] << " "; } cout << endl; } cout << endl << endl; //打印辅助数组 cout << "辅助数组中所有入队的坐标" << endl; for (int i = 0; i < _row; i++) { for (int j = 0; j < _col; j++) { printf("%2d ",_book[i][j]); } cout << endl; } } template<typename T> void Maze<T>::PrintPath() //打印迷宫路径 { while (!_s.Empty()) { cout << "(" << _s.Top().x << "," << _s.Top().y << ")" << endl; _s.Pop(); } } void test() { Maze<int> m; m.SetMap(); cout << (m.SearchPath() != 0 ? "YES":"NO")<< endl; m.PrintMap(); m.PrintPath(); } int main() { test(); system("pause"); return 0; }
下面为大家在介绍一下简单迷宫游戏的简单实现:
要实现迷宫游戏需要具备一下条件:
1、地图
可以开辟一个二维数组,然后用随机数生成地图,为了保证地图的起点和终点不被用,
所以在随机生成的地图后再将起点和终点单独拿出来设置。
2、控制键
通过方向键来获得小人下一步要走的位置,然后拿到这个位置,判断是否越界和是
否能够通过。获得位置的函数如下:
//分别按上下左右时,a2的值分别是72 80 75 77 a1的值都是224
#include<conio.h>
int a1 = _getch();
int a2 = _getch();
3、重绘
为了使得小人能够动起来,我们在得到小人下一步的坐标时,就要先将当前位置的
小人先擦除掉,然后再在下个坐标将小人重新绘制出。
获得光标位置的函数如下:
//调用move函数,给move传一个坐标,则光标就跳转到坐标处
//对于光标来说,他在显示屏上的坐标是,以左上角为原点,横坐标为x,向右增长
//纵坐标为y向下增长
#include<Windows.h>
void move(int x, int y)
{
COORD cd;
cd.X = x;
cd.Y = y;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(handle, cd);
}
#include<iostream> #include<cassert> #include<conio.h> #include<Windows.h> #include<time.h> using namespace std; void move(int x, int y) { COORD cd; cd.X = x; cd.Y = y; HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(handle, cd); } //声明一个坐标类型 typedef struct { int x; int y; }Coor; void InitMap(char **map,int line, int row) { Coor start, end; //start是起点坐标 end是终点坐标 start.x = 1; start.y = 0; end.x = line - 2; end.y = row - 1; //初始化地图 for (int i = 0; i < line; i++) { for (int j = 0; j < row; j++) { map[i][j] = ' '; } } //打印边框墙 for (int i = 0; i < line; i++) { map[i][0] = '#'; map[i][row-1] = '#'; } for (int i = 0; i <row; i++) { map[0][i] = '#'; map[line-1][i] = '#'; } //产生随机障碍物 int count = (line*row)/3; srand((unsigned)time(NULL)); while (--count) { int i = 0; int j = 0; i = rand() % (line-3) + 1; j= rand() % (row-3) + 1; map[i][j] = '#'; } //保证起点和终点不被占用 map[start.x][start.y] = ' '; map[end.x][end.y] =' '; map[start.x][start.y+1] = ' '; map[start.x][start.y +2] = ' '; map[end.x][end.y-1] = ' '; //打印地图 move(0,0); for (int i = 0; i < line; i++) { for (int j = 0; j <row; j++) { cout << map[i][j]; } cout << endl; } } void PlayGame(char **map,int line,int row) { Coor start, end; //start是起点坐标 end是终点坐标 start.x =0; start.y =1; end.x = row-1; end.y =line-2; InitMap(map,line,row); //初始化地图 //初始化猪脚 int x = start.x; int y = start.y; move(x, y); cout << 'T'; //开始玩游戏 while (1) { int a1 = _getch(); if (a1 == 48) { return; } int a2 = _getch(); move(x,y); cout << ' '; switch (a2) { case 72: y--; if (y < 1 || map[y][x] == '#') { y++; } break; case 80: y++; if ((y>line - 2) || map[y][x] == '#') { y--; } break; case 75: x--; if (x < 1 || map[y][x] == '#') { x++; } break; case 77: x++; if (map[y][x] == '#') { x--; } break; default: break; } if ((end.x == x) && (end.y == y)) { break; } move(x, y); cout << 'T'; } move(row, line); cout << "you are Win!!!" << endl; Sleep(1000); } void menu() { cout << "******************************" << endl; cout << "***Welecom to labyrinth maze**" << endl; cout << "说明:" << endl; cout << "移动:上下左右"<<endl; cout << "#:墙壁,猪脚无法穿墙"<<endl; cout << "若迷宫无法通过" << endl; cout << "按0重新生成地图" << endl; cout << "处于菜单选项时按0退出" << endl; Sleep(1000); cout << " Please choose model" << endl << endl; cout << " -->1、classic model" << endl; cout << " -->2、difficulty model" << endl; } char** Capacity(int line,int row) { char **map = (char**)malloc(line*sizeof(char*)); for (int i = 0; i <line; i++) { map[i] = (char *)malloc(row*sizeof(char)); } return map; } void DesMap(char** map,int line,int row) { move(0,0); for (int i = 0; i < line; i++) { for (int j = 0; j <row; j++) { cout<<" "; } cout << endl; } free(map); } void test() { int line = 0; int row = 0; char** map =NULL; int n = 1; while (1) { menu(); cin >> n; switch (n) { case 0: exit(1); break; case 1: line = 20; row = 40; map = Capacity(line,row); break; case 2: line =40; row = 60; map = Capacity(line,row); break; default: line = 40; row = 60; map = Capacity(line, row); break; } PlayGame(map,line,row); Sleep(1000); DesMap(map,line,row); } } int main() { test(); system("pause"); return 0; }
相关文章推荐
- 排序算法 & 迷宫的深度, 广度优先
- 迷宫问题的求解方式:应用深度优先和广度优先的搜索
- 【游戏编程】AI-迷宫寻路算法-深度优先搜索和广度优先搜索
- 栈与队列的应用——深度、广度优先搜索迷宫出口
- 迷宫最短路径深度优先
- 栈:深度优先搜索与回溯算法求解迷宫
- (广度优先搜索第一课)迷宫的最短路径 - BFS
- 图的遍历-(深度优先&广度优先)
- 笔试算法题(10):深度优先,广度优先以及层序遍历 & 第一个仅出现一次的字符
- 利用非循环顺序队列采用广度搜索法求解迷宫问题(一条路径)
- 第五讲 树-1(广度优先遍历二叉树&深度优先遍历二叉树)
- 广度优先搜索求解迷宫问题
- 广度优先搜索求解迷宫问题
- [深度优先]求迷宫中包含最多珠宝的路径
- 迷宫最短路径的C++实现(队列:广度优先)
- 树的非递归遍历(深度优先:前|中|后序遍历) & (广度优先:层次遍历)
- 广度优先搜索求解迷宫问题
- algo3-4-3.c 利用非循环顺序队列采用广度搜索法求解迷宫问题(一条路径)
- 图与路径(深度优先,广度优先,最小生成树)
- 迷宫深度优先搜索 找出一条可行路径