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

来源:互联网 发布:tps5430数据手册 编辑:程序博客网 时间:2024/05/03 14:30
  求解迷宫的通道是数据结构中一个很经典的问题,解决这个问题大概有两种思路,一

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

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



深度优先:

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

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


#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;}


5 0