您的位置:首页 > 其它

迷宫路径&广度优先求解&深度优先求解&迷宫游戏

2016-09-13 18:57 417 查看
  求解迷宫的通道是数据结构中一个很经典的问题,解决这个问题大概有两种思路,一

种是深度优先,一种是广度优先。深度优先是只要找到一条通道就可以了,而广度优先

则是寻找最短的路径。下面我们来对这两种思想做一个剖析。

深度优先:

    如果要用一句俗语来解释深度优先的话,那么一定是“不撞南墙不回头”。深度优先的重点于“深”,找到一条路就一头扎下去,直到走不通才回头。由于这里有了回退的要求,所以就要借用栈来实现。

用深度优先来求解迷宫路径的算法:



#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: