迷宫求解的非递归算法应用了之前的栈结构
https://blog.csdn.net/DuckyLoser/article/details/79914855
#include<stdio.h>
#include"seqstack.h"
#define ROW 6 //行
#define COL 6 //列
typedef struct Maze
{
int map[ROW][COL];
}Maze;
void MazeInit(Maze* maze)
{
int map[ROW][COL] = {
{0,1,0,0,0,0},
{0,1,1,1,0,0},
{0,1,0,1,0,0},
{0,1,0,1,1,0},
{0,1,1,0,0,0},
{0,0,1,0,0,0},
};
int i = 0;
for(;i<ROW;++i)
{
int j = 0;
for (;j<COL;++j)
{
maze->map[i][j] = map[i][j];
}
}
}
void MazePrint(Maze* maze)
{
int i = 0;
for(;i<ROW;++i)
{
int j = 0;
for(;j<COL;++j)
{
printf("%2d ",maze->map[i][j]);
}
printf("\n");
}
}
//1.简单迷宫存在路径(递归)
//判断pt这个点是否能落脚//能1 不能0int CanStay(Maze* maze,Point pt){ //1;如果pt在地图外,不能 if(pt.row < 0 || pt.row >= ROW || pt.col < 0 || pt.col >= COL) { return 0; } //2.在地图内,位置为1 能落脚,0,2都不能落脚 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 == ROW - 1 || cur.col == 0 || cur.col == 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.能落脚,给当前位置做标记,(标记为2) Mark(maze,cur); //3.如果当前点是出口,找到一条路,探测结束。 if(IsExit(maze,cur,entry)) { printf("Find a road!\n"); return; } //4.如果不是出口,顺时针方向探测相邻点,递归调用自身, //更新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); //第二个参数表示当前走到的点,第三个参数是入口点}
//非递归 求出口
void GetPathByloop(Maze* maze,Point entry)
{
//1.创建一个栈,并初始化,栈保存走过的路径
SeqStack stack;
Init(&stack);
//2.判定入口能不能落脚,若不能 非法参数
if(!CanStay(maze,entry))
{
return;
}
//3.标记入口点,将入口点入栈
Mark(maze,entry);
Push(&stack,entry);
//4.进入循环,获取到当前的栈顶元素
while(1)
{
//5.按顺时针方向进行取相邻点,并判定能否落脚
//如果能落脚,标记入栈,立即进入循环
//6.若相邻点都不能落脚,出栈当前点
Point cur;
int ret = front(&stack,&cur);
if(ret == 0)
{
return;
}
if(IsExit(maze,cur,entry))
{
printf("Find a road!\n");
return;
}
Point up = cur;
up.row -= 1;
if(CanStay(maze,up))
{
Mark(maze,up);
Push(&stack,up);
continue;
} Point right = cur;
right.col += 1;
if(CanStay(maze,right))
{
Mark(maze,right);
Push(&stack,right);
continue;
}
Point down = cur;
down.row += 1;
if(CanStay(maze,down))
{
Mark(maze,down);
Push(&stack,down);
continue;
}
Point left = cur;
left.col -= 1;
if(CanStay(maze,left))
{
Mark(maze,left);
Push(&stack,left);
continue;
}
}
}
//多条路,求最短路径
void MazeInitShortPath(Maze* maze)
{
int map[ROW][COL] = {
{0,1,0,0,0,0},
{0,1,1,1,0,0},
{0,1,0,1,1,1},
{1,1,1,0,0,0},
{0,0,1,0,0,0},
{0,0,1,0,0,0},
};
int i = 0;
for(;i<ROW;++i)
{
int j = 0;
for (;j<COL;++j)
{
maze->map[i][j] = map[i][j];
}
}
}
//尝试找到所有的路径,并且找到最短的一条
//
void _GetShortPath(Maze* maze,Point cur,Point entry,SeqStack* cur_path,SeqStack* short_path)
{
//1.判定当前点是否能落脚
if(!CanStay(maze,cur))
{
return;
}
//2.如果能落脚,就对当前点进行标记。同时把当前点插入到cur_path,
Mark(maze,cur);
Push(cur_path,cur);
//3.判定当前点是否是出口
if(IsExit(maze,cur,entry))
{
//a。如果当前点是出口,找到了一条路径,
//就拿当前路径和short_path中的路径进行比较,
//如果当前路径比short_path短,或者short_path本身是一个空栈,就用当前路径替换short_path,完毕之后也需要进行回溯
printf("找到了一条路径\n");
if(cur_path->size < short_path->size || short_path->size == 0)
{
printf("找到了一条比较短路径\n");
SeqStackAssgin(cur_path,short_path);
}
//b。如果没有比short_path短的话,就要尝试找其他路径(进行回溯),在回溯之前,要把cur_path栈顶元素出栈
Pop(cur_path);
return;
}
//4.当前点不出口,尝试探测四个方向。(顺时针)
Point up = cur;
up.row -= 1;
_GetShortPath(maze,up,entry,cur_path,short_path);
Point right = cur;
right.col += 1;
_GetShortPath(maze,right,entry,cur_path,short_path);
Point down = cur;
down.row += 1;
_GetShortPath(maze,down,entry,cur_path,short_path);
Point left = cur;
left.col -= 1;
_GetShortPath(maze,left,entry,cur_path,short_path);
//5.如果四个方向都递归的探测过了,就进行出栈(当前指函数栈帧结束,同时cur_path也要出栈),回溯到上一个点
Pop(cur_path);
return;
}
void GetShortPath(Maze* maze,Point entry)
{
//思路类似于数组中找到最小元素
SeqStack cur_path;//保存着当前找到的路径
SeqStack short_path;//保存着最短路径
Init(&cur_path);
Init(&short_path);
_GetShortPath(maze,entry,entry,&cur_path,&short_path);
//打印栈中的内容
SeqStackDebugPrint(&short_path,"最短路径是");
借助一个栈来完成非递归迷宫求解,所以补充几个函数。
seqstack.h
#define FOR_MAZE
#ifdef FOR_MAZE
typedef struct Point
{
int row;
int col;
}Point;
typedef Point stacktype;
#else
typedef char stacktype;
#endif
void SeqStackAssgin(SeqStack* cur_path,SeqStack* short_path);
#ifdef FOR_MAZE
void SeqStackDebugPrint(SeqStack* stack,const char* msg);
#endif
seqstack.c
void SeqStackAssgin(SeqStack* from,SeqStack* to)
{
//为了保证to里面的内存能够足够容纳from中的元素
//采取以下策略
//1.释放to中的原有内存
Destory(to);
//2.根据from元素的个数,确定内存申请的大小
//给to重新申请一个足够的内存
to->size = from->size;
to->capacity = from->capacity;
to->data = (stacktype*)malloc(to->capacity * sizeof(stacktype));
//3.在进行数据拷贝
size_t i = 0;
for (;i<from->size;++i )
{
to->data[i] = from->data[i];
}
return;
}
#ifdef FOR_MAZE
//此函数仅用于迷宫问题中,用来调试。
void SeqStackDebugPrint(SeqStack* stack,const char* msg)
{
printf("[%s]\n",msg);
size_t i = 0;
for (;i<stack->size;++i)
{
printf("(%d,%d)\n",stack->data[i].row,stack->data[i].col);
}
printf("\n");
}
#endif
//多条路,且路径带环
void MazeInitShortPathWithCycle(Maze* maze)
{
int map[ROW][COL] = {
{ 0, 1, 0, 0, 0, 0},
{ 0, 1, 1, 1, 0, 0},
{ 0, 1, 0, 1, 1, 1},
{ 1, 1, 1, 1, 0, 0},
{ 0, 0, 1, 0, 0, 0},
{ 0, 0, 1, 0, 0, 0},
};
int i = 0;
for(;i<ROW;++i)
{
int j = 0;
for (;j<COL;++j)
{
maze->map[i][j] = map[i][j];
}
}
}
int CanStayWithCycle(Maze* maze,Point cur,Point pre)
{
//1.当前点是否在地图上
if(cur.row < 0 || cur.row >= ROW || cur.col < 0 || cur.col >= COL)
{
return 0;
}
//2.当前点是不是墙
int cur_value = maze->map[cur.row][cur.col];
if(cur_value == 0)
{
return 0;
}
//3.当前点是1,可以直接落脚
if(cur_value == 1)
{
return 1;
}
//4.如果当前点已经走过,比较cur和pre对应值的大小关系
//取出pre_value之前,要判定pre点是不是一个非法点
if(pre.row == -1 && pre.col == -1)
{
//如果是非法点,就只有一种情况
//但是这种情况又不需要考虑,
//如果pre为非法点,说明cur是入口点
//判定入口点是否能落脚,和pre没关系,
//直接判定cur_value是0还是1就行,以上都进行过判定
}
int pre_value = maze->map[pre.row][pre.col];
//a。cur_value 7,pre_value 5 可以落脚
//b。cur_value 6,pre_value 5 不应该落脚
//当cur_value > pre_value + 1 就应该落脚
if(cur_value > pre_value + 1)
{
return 1;
}
return 0;
}
void MarkWithCycle(Maze* maze,Point cur,Point pre)
{
if(pre.row == -1 && pre.col == -1)
{
//针对入口点进行标记,此时pre是非法点
//不能根据pre_value + 1 的方式来标记
maze->map[cur.row][cur.col] = 2;
return;
}
int pre_value = maze->map[pre.row][pre.col];
maze->map[cur.row][cur.col] = pre_value + 1;
}
void _GetShortPathWithCycle(Maze* maze,Point cur,Point pre,Point entry,SeqStack* cur_path,SeqStack* short_path)
{
//1.判定当前点是否能落脚(判定规则变了)
if(!CanStayWithCycle(maze,cur,pre))
{
return;
}
//2.标记当前点(标记规则也变了),并且插入到cur_path
MarkWithCycle(maze,cur,pre);
Push(cur_path,cur);
//3.判定当前点是否是出口,
if(IsExit(maze,cur,entry))
{
printf("找到了一条路径\n");
//如果当前点是出口,要拿cur_path和short_path进行PK,将比较短的保存到short_path
if(cur_path->size < short_path->size || short_path->size == 0)
{
printf("找到了一条比较短路径\n");
SeqStackAssgin(cur_path,short_path);
}
//进行回溯,不管是不是最短路径都要回溯
Pop(cur_path);
}
//4.如果不是出口,以当前点为基准点,四个方向进行探测
Point up = cur;
up.row -= 1;
_GetShortPathWithCycle(maze,up,cur,entry,cur_path,short_path);
Point right = cur;
right.col += 1;
_GetShortPathWithCycle(maze,right,cur,entry,cur_path,short_path);
Point down = cur;
down.row += 1;
_GetShortPathWithCycle(maze,down,cur,entry,cur_path,short_path);
Point left = cur;
left.col -= 1;
_GetShortPathWithCycle(maze,left,cur,entry,cur_path,short_path);
//5.如果都探测过了,就进行回溯
Pop(cur_path);
return;
}
void GetShortPathWithCycle(Maze* maze,Point entry)
{
SeqStack cur_path;
SeqStack short_path;
Init(&cur_path);
Init(&short_path);
Point pre = {-1,-1};
_GetShortPathWithCycle(maze,entry,pre,entry,&cur_path,&short_path);
SeqStackDebugPrint(&short_path,"最短路径是");
}