迷宫问题——回溯法(栈)C++
思路:
从入口出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路退回,换一个方向再继续探索,直至所有可能的通路都探索到为止。为了保证在任何位置上都能沿原路退回(回溯),显然需要用一个后进先出的结构来保存从入口到当前位置的路径。因此,在求迷宫通路的算法中要应用“栈”的思想。假设“当前位置”指的是“在搜索过程中的某一时刻所在图中某个方块位置”,则求迷宫中一条路径的算法的基本思想是:若当前位置“可通”,则纳入“当前路径”,并继续朝“下一位置”探索,即切换“下一位置”为“当前位置”,如此重复直至到达出口;若当前位置“不可通”,则应顺着“来向”退回到“前一通道块”,然后朝着除“来向”之外的其他方向继续探索;若该通道块的四周8个方块均“不可通”,则应从“当前路径”上删除该通道块。所谓“下一位置”指的是当前位置四周8个方向(上、下、左、右、左上、左下、右上、右下)上相邻的方块。假设以栈S记录“当前路径”,则栈顶中存放的是“当前路径上最后一个通道块”。由此,“纳入路径”的操作即为“当前位置入栈”;“从当前路径上删除前一通道块”的操作即为“出栈”。
数据结构设计考虑:
(1)建立二维数组表示迷宫的路径(0表示通道,1表示墙壁,数组矩阵增加外圈墙壁,保证每点都有8个移动方向);
1 1 1 1 1 1 1 1 1 1
1 0 1 1 1 0 1 1 1 1
1 1 0 1 0 1 1 1 1 1
1 0 1 0 0 0 0 0 1 1
1 0 1 1 1 0 1 1 1 1
1 1 0 0 1 1 0 0 0 1
1 0 1 1 0 0 1 1 0 1
1 1 1 1 1 1 1 1 1 1
(2)创建一个栈,用来存储“当前路径”,即“在搜索过程中某一时刻所在图中某个方块位置”。
逻辑结构存储结构:
(1)创建一个Int类型的二维数组int maze[n1][n2],用来存放0和1 ;
(2)创建一个结构体用来储存数组信息(数组的横坐标X,数组的纵坐标Y,方向C)
typedef struct node
{
int x;
int y;
int c;
}StackNode;
#include <iostream>
#include <stack>
using namespace std;
//栈节点
typedef struct node
{
int x; //迷宫数组横坐标
int y; //迷宫数组纵坐标
int c; //前进方向
}StackNode;
//linkstack top[n1*n2];
//初始化迷宫数组
const int xlen=6,ylen=8; //xlen 行数,ylen 列数
int maze[xlen+2][ylen+2]={
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 0, 1, 1, 1, 0, 1, 1, 1, 1},
{ 1, 1, 0, 1, 0, 1, 1, 1, 1, 1},
{ 1, 0, 1, 0, 0, 0, 0, 0, 1, 1},
{ 1, 0, 1, 1, 1, 0, 1, 1, 1, 1},
{ 1, 1, 0, 0, 1, 1, 0, 0, 0, 1},
{ 1, 0, 1, 1, 0, 0, 1, 1, 0, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
};
int mark[xlen+2][ylen+2]; // 访问标记数组
StackNode path[xlen*ylen]; //存储访问路径
struct Offsets {
int a, b; // a和b是x,y方向的偏移
};
/*定义移动方向偏移
(i,j)是当前位置
西北 北 东北
(i-1,j-1) (i-1,j) (i-1,j+1)
西 (i ,j-1) (i ,j) (i ,j+1) 东
(i+1,j-1) (i+1,j) (i+1,j+1)
西南 南 东南
*/
Offsets direction[8] = {
{-1, 0}, // 北 0
{-1, 1}, // 东北 1
{0, 1}, // 东 2
{1, 1}, // 东南 3
{1, 0}, // 南 4
{1, -1}, // 西南 5
{0, -1}, // 西 6
{-1, -1} // 西北 7
};
void Path(int x, int y, int m, int n) //x, y为入口坐标;m,n为出口坐标
{
int cur_i, cur_j, next_i, next_j; //当前坐标(cur_i,cur_j),移动到下一位置坐标(next_i,next_j)
int d;//移动方向
int k=0;
stack<StackNode> st; // 定义一个名为st的栈,栈中元素类型为 StackNode
StackNode temp;
mark[x][y] = 1; // 标记入口地址已访问
temp.x=x;
temp.y=y;
temp.c=0 ;//从北开始
st.push(temp); // 把入口坐标进栈
while(!st.empty())
{
temp = st.top(); // 取栈顶元素
st.pop(); // 栈顶元素出栈
cur_i = temp.x;
cur_j = temp.y; // 取得当前位置坐标
d = temp.c; // 方向
while(d < 8) // 找下一位置(next_i,next_j)
{
next_i = cur_i + direction[d].a;
next_j = cur_j + direction[d].b;
if(next_i == m && next_j == n) // 到达出口
{
while(!st.empty()) // 输出栈中存的路径
{
temp=st.top(); // 取栈顶元素
path[k].x = temp.x;
path[k].y = temp.y;
path[k].c = temp.c;
st.pop(); // 出栈
k++;
}
//输出路径
for(int i=k-1;i>=0;i--)
{
cout <<"(" <<path[i].x << "," << path[i].y << "), " << path[i].c << endl;
}
cout << "(" << m << "," << n << "), " << d << endl;
return;
}
// 未到达出口
if(maze[next_i][next_j] == 0 && mark[next_i][next_j] == 0) // 如果通过且未被访问过
{
mark[next_i][next_j] = 1; // 标记为已访问过
temp.x = cur_i;
temp.y = cur_j;
temp.c = d; // 记录已走过的位置和前进的方向
st.push(temp); // 进栈
cur_i = next_i;
cur_j = next_j;
d = 0; // 移动到(next_i,next_j),在各个方向试探
}
else d++; // 试探下一个方向
} // 内while
} // 外while
cout << "没找到从入口到出口的路径!" << endl;
}
int main()
{
int i, j, x, y, m, n; //x, y为入口坐标;m,n为出口坐标
//初始化访问标记数组
for(i = 0; i < xlen; i++)
for(j = 0; j < ylen; j++)
{
mark[i][j] = 0;
}
cout << "请输入入口位置坐标:";
cin >> x >> y;
cout << "请输入出口位置坐标:";
cin >> m >> n;
Path(x, y, m, n); // 调用求解迷宫问题函数
return 0;
}