如图是一个有三个出口的迷宫,现在来求它的最短路径(不带环)
思想:定义两个栈,一个栈存放当前路径。一个栈存最短路径,每次都将当前路径和最短路径比较如果当前路径小于最短路径,那就交换两个栈中的元素
代码实现:
void MazeInit2(Maze* maze)
{
if(maze == NULL)
{
return;
}
int map[MAX_ROW][MAX_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}
};
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 _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);
SeqStackPush(cur_path,cur);
//判断当前点是否是出口
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);
//printf("cur :%d,%d\n",cur.row,cur.col);
}
//b)如果当前路径没有short_path短,就继续再找其他路径(进行回溯)
// 回溯之前要把cur_path栈顶元素出栈
SeqStackPop(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,也要进行出栈),回溯到上一个点
SeqStackPop(cur_path);
return;
}
//找到所有路径,然后从所有的路径中筛选出一条最短的(递归)
void GetShortPath(Maze* maze,Point entry)
{
//就像从一个数组中查找最小元素,一样,定义一个short_path保存最短路径
SeqStack cur_path;//保存当前路径
SeqStack short_path;//保存最短路径
SeqStackInit(&cur_path);
SeqStackInit(&short_path);
_GetShortPath(maze,entry,entry,&cur_path,&short_path);
SeqStackDebugPrint(&short_path,"最短路径");
}
seqstack.c中的代码实现
#ifdef FOR_MAZE
//#include<stdio.h>
//#include"seqstack.h"
//此函数仅用于迷宫问题,用来调试
//通常意义下,栈是不允许遍历的
//但是如果进行调试或者测试,就不一样了
//因此再这里虽然函数进行遍历了,但是仅用于调试
//之所以写这个函数遍历栈,是为了能够从入口到出口的顺序来打印栈的内容
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");
}
void SeqStackAssgin(SeqStack* from,SeqStack* to)
{
//采用下面的方法保证to里面的内存能够足够容纳from中的元素
//1.释放to中的原有内存
SeqStackDestroy(to);
//2.根据from元素的个数,确定内存申请内存的大小,给to重新申请一个足够的内存
to->size = from->size;
to->capacity = from->capacity;
to->data = (SeqStackType*)malloc(to->capacity * sizeof(SeqStackType));
//3.进行数据拷贝
size_t i = 0;
for(;i < from->size ; ++i)
{
to->data[i] = from->data[i];
}
return;
}
#endif
测试代码
void Test3()
{
TEST_HEADER;
Maze maze;
MazeInit2(&maze);
Point entry = {0,1};
GetShortPath(&maze,entry);
MazePrint(&maze);
}
运行结果:
如图是一个有三个出口的迷宫,现在来求它的最短路径(带环)
因为这个迷宫带环路径较多就不一一画出来了
第一次标记的结果
第二次标记的结果
第三次标记的结果
第四次标记结果
第五次标记结果
具体看代码实现
void MazeInit3(Maze* maze)
{
if(maze == NULL)
{
return;
}
int map[MAX_ROW][MAX_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}
};
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;
}
int CanStayWithCycle(Maze *maze,Point cur,Point pre)
{
if(maze == NULL)
{
return 0;
}
//当前点是否在地图上
if(cur.row < 0 || cur.row >= MAX_ROW || cur.col < 0 || cur.col >= MAX_COL)
{
return 0;
}
//当前点不是墙
int cur_value = maze->map[cur.row][cur.col];
if(cur_value == 0)
{
return 0;
}
//如果当前点是1,就可以直接落脚
if(cur_value == 1)
{
return 1;
}
//当前点若已经被标记就比较当前点对应的值和pre对应的值的大小
//a)cur_path 7,pre_value 5,应该落脚
//b)cur_path 6,pre_value 5,不应该落脚
//c)cur_path 5,pre_value 5,不应该落脚
//d)cur_path 4,pre_value 5,不应该落脚
//总结以上的几种情况可以总结出:cur_path > pre_path + 1就应该落脚
int pre_value = maze->map[pre.row][pre.col];
if(cur_value > pre_value+1)
{
return 1;
}
return 0;
}
void MarkWithCycle(Maze* maze,Point cur,Point pre)
{
if(maze == NULL)
{
return;
}
if(cur.row < 0 || cur.row >= MAX_ROW || cur.col < 0 || cur.col >= MAX_COL)
{
return;
}
// if(pre.row < 0 || pre.row >= MAX_ROW || pre.col < 0 || pre.col >= MAX_COL)
// {
// return;
// }
if(pre.row == -1 && pre.col == -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;
return;
}
void _GetShortPathWithCycle(Maze* maze,Point cur,Point pre,Point entry,SeqStack* cur_path,SeqStack* short_path)
{
//判定当前是否能落脚(判定规则变了)
printf("cur :(%d,%d)\n",cur.row,cur.col);
if(!CanStayWithCycle(maze,cur,pre))
{
return;
}
//标记当前点,并且将当前点插入到cur_path
MarkWithCycle(maze,cur,pre);
SeqStackPush(cur_path,cur);
pre = cur;
//判断当前点是否是出口
if(IsExit(maze,cur,entry))
{
printf("找到了一条路径!\n");
//如果是出口,要拿cur_path和short_path进行比较,把比较短的路径保存到short_path中
if(cur_path->size < short_path->size || short_path->size == 0)
{
printf("找到了一条比较短的路径!\n");
SeqStackAssgin(cur_path,short_path);
}
//进行回溯,不管当前找到的这条路径是否是比较短的路径,都要进行回溯
SeqStackPop(cur_path);
return;
}
//如果不是出口,以当前点为基准点,探测周围的四个点
Point up = cur;
up.row -= 1;
_GetShortPathWithCycle(maze,up,pre,entry,cur_path,short_path);
Point right = cur;
right.col += 1;
_GetShortPathWithCycle(maze,right,pre,entry,cur_path,short_path);
Point down = cur;
down.row += 1;
_GetShortPathWithCycle(maze,down,pre,entry,cur_path,short_path);
Point left = cur;
left.col -= 1;
_GetShortPathWithCycle(maze,left,pre,entry,cur_path,short_path);
//如果四个点都探测过了,就出栈回溯
SeqStackPop(cur_path);
return;
}
void GetShortPathWithCycle(Maze* maze,Point entry)
{
SeqStack cur_path;
SeqStack short_path;
SeqStackInit(&cur_path);
SeqStackInit(&short_path);
Point pre = {-1,-1};
_GetShortPathWithCycle(maze,entry,pre,entry,&cur_path,&short_path);
SeqStackDebugPrint(&short_path,"最短路径为");
}
测试代码
void Test4()
{
TEST_HEADER;
Maze maze;
MazeInit3(&maze);
Point entry = {0,1};
GetShortPathWithCycle(&maze,entry);
MazePrint(&maze);
}
运行结果