在数据结构中,迷宫问题属于中等难度的问题。在这里我讲一下我对于求解迷宫问题的理解(看课本中来,然后再自己好好理解)
首先从起始点出发,对该位置的四个方向(上右下左)进行探索,若某一个位置可以通过并且该位置是第一次走则进入该位置并把该位置进栈,标记该位置已经走过了,重复这个过程。若走进一个死胡同,则一步一步退出来(出栈处理),若退出来的过程栈空了,说明无法从起点位置找到出口。若前进过程中的某一点是出口位置,则表示找到出口了,把走的路径输出并结束。
这里用到一个栈,是用来存储走过的位置的。栈的结构如下:
const int stackIncreament=20;
template<class T>
class Stack
{
public:
Stack(int sz=50);
~Stack()
{
delete []elements;
}
void Push(const T&x);
bool Pop(T&x);
bool getTop(T&x);
bool IsEmpty()const
{
return (top==-1)?true:false;
}
bool IsFull()const
{
return (top==maxSize-1)?true:false;
}
int getSize()const
{
return top+1;
}
void makeEmpty()
{
top=-1;
}
friend ostream& operator<<(ostream& os, Stack<T>& s);
void output()
{
for(int i=0; i<=top; i++)cout<<elements[i].x<<" "<<elements[i].y<<endl;
}
private:
T *elements;
int top;
int maxSize;
void overflowProcess();//当数组不够存放数据时,对数组容量进行扩充
};
template<class T>
Stack<T>::Stack(int sz):top(-1),maxSize(sz)
{
elements=new T[maxSize];
assert(elements!=NULL);
}
template<class T>
void Stack<T>::overflowProcess()
{
//私有函数,扩充栈的存储空间
T *newArray = new T[maxSize+stackIncreament];
if(newArray==NULL)
{
cerr<<"存储分配失败!"<<endl;
exit(1);
}
for(int i=0; i<maxSize; i++)newArray[i]=elements[i];
maxSize=maxSize+stackIncreament;
delete []elements;
elements=newArray;
}
template<class T>
void Stack<T>::Push(const T&x)
{
if(IsFull())overflowProcess();
elements[++top]=x;
}
template<class T>
bool Stack<T>::Pop(T& x)
{
if(IsEmpty())return false;
x=elements[top--];
return true;
}
template<class T>
bool Stack<T>::getTop(T& x)
{
if(IsEmpty())return false;
x=elements[top];
return true;
}
template<class T>
ostream& operator<<(ostream& os, Stack<T> &s) //重载输出
{
os<<"top="<<s.top<<endl;
for(int i=0; i<s.top+3; i++)os<<"第"<<i<<"个元素:"<<s.elements[i]<<endl;
return os;
}
对于迷宫,要用一个二维数组maze[][]来存储它的形状,墙位置用1表示,有路位置用0表示。
还要一个二维数组mark[][]来表示某个有路位置是否已经被走过了。1表示该位置已经被走过或者不可以走了,0表示该位置没被走过可以走。
static int **mark;//用来标记某个位置是否被访问过了
static int **Maze;
用一个结构体items表示当前位置的信息
struct items
{
int x, y, dir;
};
用一个结构体表示四个方向(上右下左):
struct offsets
{
int a, b;
};
offsets Move[4];
四个方向的初始化:
Move[0].a=-1;
Move[0].b=0;
Move[1].a=0;
Move[1].b=1;
Move[2].a=1;
Move[2].b=0;
Move[3].a=0;
Move[3].b=-1;
先输入迷宫的行和列,再输入迷宫形状,再输入起始点坐标和重点位置坐标:
cout << "请输入行和列" << endl;
cin>>row>>col;
cout<<"请输入迷宫的形状"<<endl;
Maze=new int*[row];
mark=new int*[row];
int row, col;
int end_x, end_y;
int begin_x, begin_y;
cout<<"请输入起始点位置:"<<endl;
cin>>begin_x>>begin_y;
cout<<"请输入终点位置:"<<endl;
cin>>end_x>>end_y;
以下是完整的代码:
#include <iostream>
#include <stdlib.h>
#include <assert.h>
using namespace std;
//迷宫问题的求解
const int stackIncreament=20;
int sum=0;
int row, col;
int end_x, end_y;
int begin_x, begin_y;
template<class T>
class Stack
{
public:
Stack(int sz=50);
~Stack()
{
delete []elements;
}
void Push(const T&x);
bool Pop(T&x);
bool getTop(T&x);
bool IsEmpty()const
{
return (top==-1)?true:false;
}
bool IsFull()const
{
return (top==maxSize-1)?true:false;
}
int getSize()const
{
return top+1;
}
void makeEmpty()
{
top=-1;
}
friend ostream& operator<<(ostream& os, Stack<T>& s);
void output()
{
for(int i=0; i<=top; i++)
cout<<"("<<elements[i].x<<","<<elements[i].y<<") ";
}
private:
T *elements;
int top;
int maxSize;
void overflowProcess();//当数组不够存放数据时,对数组容量进行扩充
};
template<class T>
Stack<T>::Stack(int sz):top(-1),maxSize(sz)
{
elements=new T[maxSize];
assert(elements!=NULL);
}
template<class T>
void Stack<T>::overflowProcess()
{
//私有函数,扩充栈的存储空间
T *newArray = new T[maxSize+stackIncreament];
if(newArray==NULL)
{
cerr<<"存储分配失败!"<<endl;
exit(1);
}
for(int i=0; i<maxSize; i++)newArray[i]=elements[i];
maxSize=maxSize+stackIncreament;
delete []elements;
elements=newArray;
}
template<class T>
void Stack<T>::Push(const T&x)
{
if(IsFull())overflowProcess();
elements[++top]=x;
}
template<class T>
bool Stack<T>::Pop(T& x)
{
if(IsEmpty())return false;
x=elements[top--];
return true;
}
template<class T>
bool Stack<T>::getTop(T& x)
{
if(IsEmpty())return false;
x=elements[top];
return true;
}
template<class T>
ostream& operator<<(ostream& os, Stack<T> &s) //重载输出
{
os<<"top="<<s.top<<endl;
for(int i=0; i<s.top+3; i++)os<<"第"<<i<<"个元素:"<<s.elements[i]<<endl;
return os;
}
static int **mark;//用来标记某个位置是否被访问过了
static int **Maze;
struct items
{
int x, y, dir;
};
struct offsets
{
int a, b;
};
offsets Move[4];
void path(int m, int p)
{
int i, j, d, g, h;//i和j表示实际处在的位置,g和h表示要探测的位置
mark[1][1]=1;
Stack<items> st(200);
items tmp;
tmp.x=begin_x;
tmp.y=begin_y;
tmp.dir=0;
st.Push(tmp);//入栈
for(int i=0; i<row; i++)
{
for(int j=0; j<col; j++)
if(Maze[i][j]==0)mark[i][j]=0;
else mark[i][j]=1;
}
bool b=true;
while(!st.IsEmpty())
{
st.Pop(tmp);//当进入死胡同时,这一步可以一直退到有路走的地方
sum++;
i=tmp.x;
j=tmp.y;
d=tmp.dir;
b=true;
while(d<4)
{
g=i+Move[d].a;
h=j+Move[d].b;
if(g<0||g>=row||h<0||h>=col)
{
d++;
continue;
}
if(g==m&&h==p)
{
tmp.x=g;
tmp.y=h;
st.Push(tmp);
cout<<"走通了,走的路径如下:"<<endl;
st.output();
return;
}
if(Maze[g][h]==0&&mark[g][h]==0) //表示该位置有路且未被访问过
{
if(b==true)
{
st.Push(tmp);
}
b=false;
mark[g][h]=1;
tmp.x=g;
tmp.y=h;
tmp.dir=d;
st.Push(tmp);
i=g;
j=h;
d=0;//这一步才是真正表示位置的移动
}
else d++;//换一个方向走走
}
}
cout<<"迷宫中没有走出去的路"<<endl;
}
int main()
{
Move[0].a=-1;
Move[0].b=0;
Move[1].a=0;
Move[1].b=1;
Move[2].a=1;
Move[2].b=0;
Move[3].a=0;
Move[3].b=-1;
cout << "请输入行和列" << endl;
cin>>row>>col;
cout<<"请输入迷宫的形状"<<endl;
Maze=new int*[row];
mark=new int*[row];
for(int i=0; i<row; i++)
{
Maze[i]=new int[col];
mark[i]=new int[col];
}
for(int i=0; i<row; i++)
for(int j=0; j<col; j++)cin>>Maze[i][j];
cout<<"请输入起始点位置:"<<endl;
cin>>begin_x>>begin_y;
cout<<"请输入终点位置:"<<endl;
cin>>end_x>>end_y;
path(end_x,end_y);
return 0;
}
我用了三组测试数据:
第1组输入:
5 5
1 1 1 1 1
0 0 0 0 1
1 0 1 1 1
1 0 0 1 1
1 1 0 1 1
1 0
4 2
截图如下:
第2组数据输入:
15 15
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 1 1 1 0 0 0 1
1 0 1 0 0 0 0 0 0 0 1 0 1 0 1
1 0 1 1 1 1 1 0 1 0 1 0 1 0 1
1 0 0 0 1 0 0 0 1 0 1 0 1 0 1
1 1 1 0 1 1 1 0 1 0 1 0 1 0 1
1 0 0 0 1 0 0 0 1 0 1 0 1 0 1
1 0 1 1 1 1 1 0 1 0 1 0 1 0 1
1 0 0 0 1 0 0 0 1 0 0 0 1 0 1
1 1 1 0 1 1 1 0 1 1 1 0 1 0 1
1 0 0 0 1 0 0 0 1 0 0 0 0 1 1
1 0 1 1 1 0 1 0 1 1 1 1 0 0 1
1 0 1 1 1 1 1 1 1 1 1 1 1 1 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1 1 1 1 1 1 0 1
1 0
14 13
截图如下:
第3组数据输入:
20 20
1 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 1
0 1 1 0 1 1 1 1 0 1 1 1 1 0 0 0 0 0 0 0
0 0 1 0 1 0 0 0 0 1 0 0 1 0 1 1 1 0 1 1
1 0 1 1 0 1 0 1 0 0 1 0 1 0 0 1 0 0 0 0
1 0 1 1 0 1 1 0 0 1 0 0 1 0 1 1 1 1 0 1
1 0 0 1 0 1 0 0 1 0 1 0 1 1 1 0 0 1 0 0
1 1 0 1 1 0 0 1 1 0 0 0 0 0 0 1 0 1 0 1
0 0 0 1 0 0 1 0 0 0 1 1 1 1 1 1 0 1 0 0
0 1 0 1 1 0 1 1 1 0 1 0 0 0 0 1 0 1 0 1
0 1 1 1 0 0 1 0 0 0 1 0 1 0 1 1 0 0 0 0
0 0 0 1 0 1 1 1 1 0 1 0 1 0 1 1 0 1 1 0
1 0 1 1 1 1 0 0 0 0 1 1 0 0 0 0 0 1 1 0
1 0 0 0 0 1 1 1 1 0 1 0 1 1 1 1 0 0 1 0
1 1 1 1 0 1 0 0 0 0 1 0 1 0 0 0 1 0 0 1
1 0 0 1 0 1 1 0 1 0 1 0 1 0 1 0 1 1 0 1
1 1 0 1 0 1 0 0 1 0 1 0 0 0 1 0 0 0 0 1
1 1 0 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 0 1
0 12
18 0
截图如下:
注意事项:
1.栈的大小需要自己控制
2.记住当跳到迷宫数组时要马上换个方向探索:
if(g<0||g>=row||h<0||h>=col)
{
d++;
continue;
}