图中0表示墙,即无法落脚;1表示可以落脚;之后我们会用2来标记走过的路。
我们默认出口为四个边界上的点。若入口也为边界点,则出口与入口不能重合。
要解决这个问题,我们还是要基于栈的基本操作,依赖栈结构来辅助完成回溯法。而这里的栈可以是内存地址空间中的栈,也可以我们自己手动创建一个栈来实现。这两种方法我们在下面的代码中都会有所实现。
第一种方法:基于地址空间中的栈辅助完成回溯(递归式)
maze.h:
#pragma once
#include
#include
#include
#define MAX_ROW 6
#define MAX_COL 6
typedef struct Maze{
int map[MAX_ROW][MAX_COL];
}Maze;
typedef struct Point{
int row;
int col;
}Point;
typedef Point SeqStackType;
typedef struct SeqStack{
SeqStackType *data;
size_t size;
size_t capacity;
}SeqStack;
maze.c:
#include"maze.h"
int map[MAX_ROW][MAX_COL]={
{0,1,0,0,0,0},
{0,1,1,1,0,0},
{0,1,0,1,1,0},
{0,1,1,0,0,0},
{0,0,1,0,0,0},
{0,0,1,0,0,0}
};
void MazeInit(Maze* maze){
if(maze == NULL)
return;
size_t i = 0;
for(;i < MAX_ROW;i++){
size_t j = 0;
for(;j < MAX_COL;j++){
maze->map[i][j] = map[i][j];
}
}
return;
}
void MazePrint(Maze* maze){
if(maze == NULL)
return;
size_t i = 0;
for(;i < MAX_ROW;i++){
size_t j = 0;
for(;j < MAX_COL;j++){
printf("%2d ",maze->map[i][j]);
}
printf("\n");
}
return;
}
int CanStay(Maze* maze,Point pt){
if(pt.row < 0 || pt.row >= MAX_ROW || pt.col < 0 || pt.col >= MAX_COL){
return 0;
}
int value = maze->map[pt.row][pt.col];
if(value == 1){
return 1;
}
return 0;
}
void Mark(Maze* maze,Point cur){
maze->map[cur.row][cur.col] = 2;
}
int IsExit(Maze* maze,Point cur,Point entry){
(void)maze;
//1.判断当前点是不是入口,若为入口,则不是出口
if(cur.row == entry.row && cur.col == entry.col){
return 0;
}
//2.如果当前点在地图边界上,说明是出口
if(cur.row == 0 || cur.row == MAX_ROW-1 || cur.col == 0 || cur.col == MAX_COL-1){
return 1;
}
return 0;
}
void _GetPath(Maze* maze,Point cur,Point entry){
printf("cur:(%d,%d)\n",cur.row,cur.col);
//1.判断当前点能否落脚
if(!CanStay(maze,cur)){
return;
}
//2.若能落脚,给当前位置做一个标记
Mark(maze,cur);
//3.若当前点为出口,说明找到了一条出口,探测结束
if(IsExit(maze,cur,entry)){
printf("找到了一条出路\n");
return;
}
//4.若当前点不是出口,则按顺时针方向探测四个相邻的点,递归式调用函数自身,递归式更新cur节点
//(每次递归时,cur都是下一次要走的点,这个点能否落脚,交给递归函数作判断)
Point up = cur;
up.row -= 1;
_GetPath(maze,up,entry);
Point right = cur;
right.col += 1;
_GetPath(maze,right,entry);
Point down = cur;
down.row += 1;
_GetPath(maze,down,entry);
Point left = cur;
left.col -= 1;
_GetPath(maze,left,entry);
}
void GetPath(Maze* maze,Point entry){
if(maze == NULL){
return;
}
_GetPath(maze,entry,entry);
}
test.c:
#include "maze.h"
#define PRINT_HEADER printf("\n============%s============\n",__FUNCTION__)
void Test1(){
PRINT_HEADER;
Maze maze;
MazeInit(&maze);
Point entry = {0,1};
GetPath(&maze,entry);
MazePrint(&maze);
}
int main(){
Test1();
return 0;
}
结果演示:
第二种方法:自己手动创建一个栈,然后基于自己创建的栈实现回溯(非递归式)
maze.h:
#pragma once
#include
#include
#include
#define MAX_ROW 6
#define MAX_COL 6
typedef struct Maze{
int map[MAX_ROW][MAX_COL];
}Maze;
typedef struct Point{
int row;
int col;
}Point;
typedef Point SeqStackType;
typedef struct SeqStack{
SeqStackType *data;
size_t size;
size_t capacity;
}SeqStack;
maze.c:
#include"maze.h"
int map[MAX_ROW][MAX_COL]={
{0,1,0,0,0,0},
{0,1,1,1,0,0},
{0,1,0,1,1,0},
{0,1,1,0,0,0},
{0,0,1,0,0,0},
{0,0,1,0,0,0}
};
void MazeInit(Maze* maze){
if(maze == NULL)
return;
size_t i = 0;
for(;i < MAX_ROW;i++){
size_t j = 0;
for(;j < MAX_COL;j++){
maze->map[i][j] = map[i][j];
}
}
return;
}
void MazePrint(Maze* maze){//打印迷宫
if(maze == NULL)
return;
size_t i = 0;
for(;i < MAX_ROW;i++){
size_t j = 0;
for(;j < MAX_COL;j++){
printf("%2d ",maze->map[i][j]);
}
printf("\n");
}
return;
}
int CanStay(Maze* maze,Point pt){//判断当前点是否能落脚
if(pt.row < 0 || pt.row >= MAX_ROW || pt.col < 0 || pt.col >= MAX_COL){
return 0;
}
int value = maze->map[pt.row][pt.col];
if(value == 1){
return 1;
}
return 0;
}
void Mark(Maze* maze,Point cur){//标记走过的路径
maze->map[cur.row][cur.col] = 2;
}
int IsExit(Maze* maze,Point cur,Point entry){
(void)maze;
//1.判断当前点是不是入口,若为入口,则不是出口
if(cur.row == entry.row && cur.col == entry.col){
return 0;
}
//2.如果当前点在地图边界上,说明是出口
if(cur.row == 0 || cur.row == MAX_ROW-1 || cur.col == 0 || cur.col == MAX_COL-1){
return 1;
}
return 0;
}
void GetPathByLoop(Maze* maze,Point entry){
if(maze == NULL)
return;
//1.创建一个栈,并且初始化,这个栈保存着走过的路径
SeqStack stack;
SeqStackInit(&stack);
//2.判定入口点能否落脚,若不能,说明参数非法,则直接返回
if(!CanStay(maze,entry)){
return;
}
//3.标记入口点,并将入口点入栈
Mark(maze,entry);
SeqStackPush(&stack,entry);
//4.进入循环,获取到当前栈的栈顶元素
Point cur;
while(GetTop(&stack,&cur)){
//5.判定这个点是否为出口,若为出口,直接函数返回
if(IsExit(maze,cur,entry)){
printf("找到了一条路径\n");
return;
}
//6.按照顺时针方向取相邻点,判断相邻点能否落脚,若能落脚,就标记并入栈,立即进入下一轮循环
Point up = cur;
up.row -= 1;
if(CanStay(maze,up)){
Mark(maze,up);
SeqStackPush(&stack,up);
continue;
}
Point right = cur;
right.col += 1;
if(CanStay(maze,right)){
Mark(maze,right);
SeqStackPush(&stack,right);
continue;
}
Point down = cur;
down.row += 1;
if(CanStay(maze,down)){
Mark(maze,down);
SeqStackPush(&stack,down);
continue;
}
Point left = cur;
left.col -= 1;
if(CanStay(maze,left)){
Mark(maze,left);
SeqStackPush(&stack,left);
continue;
}
//7.若四个相邻点均不能落脚,就出栈当前点,相当于进行回溯
SeqStackPop(&stack);
}
return;
}
test.c:
#include "maze.h"
#define PRINT_HEADER printf("\n============%s============\n",__FUNCTION__)
void Test2(){
PRINT_HEADER;
Maze maze;
MazeInit(&maze);
Point entry = {0,1};
GetPathByLoop(&maze,entry);
MazePrint(&maze);
}
int main(){
Test2();
return 0;
}
结果演示:
至此,我们求解简单迷宫是否存在路径的问题就解决了。