在上篇文章中,我们重点解决迷宫寻找路径。这篇文章,我们研究的是对迷宫最短路径进行寻找。
如果一个迷宫有很多条路径,那我们该如何找到这条最短的路径呢?
我们上篇文章以及讲过,每次判断一个点是否能够落脚后,入栈,然后进一系列操作。这里我们的思路是,开始创建两个栈,一个存放每次出去的路径的过程,一次存放最短的路径,最后打出最短的路径。这里需要让栈内元素为我们的结构体Point。在每个出口比较我们两个栈的size大小,达到最终一个栈内为最短的路径。我们对上述的一些操作先做基本实现。
typedef Point SeqStackType
typedef struct SeqStack {
SeqStackType* data;
size_t capacity;
size_t size;
};
void SeqStackPrintShort(SeqStack* stack)//打印最短路径
{
if(stack == NULL) {
return;
}
size_t i = 0;
for(; i < stack->size; ++i) {
printf("(%d,%d)\n",stack->data[i].row, stack->data[i].col);
}
printf("\n");
}
void SeqStackAssign(SeqStack* from, SeqStack* to)//赋值栈,即将size较小的赋值
{
if(from == NULL || to == NULL) {
return;
}
size_t i = 0;
to->capacity = from->capacity;
to->size = from->size;
to->data = (SeqStackType*)malloc(to->capacity * sizeof(SeqStackType));
for(; i < to->size; ++i) {
to->data[i] = from->data[i];
}
return;
}
接下来就是我们对迷宫的操作实现了
int _GetShortPath(Maze* maze, Point cur, Point entry, SeqStack* cur_path, SeqStack* short_path)//获取最短路径
{
if(maze == NULL) {
return 0;
}
//判断是否能够落脚
if(!CanStay(maze, cur)) {
return 0;
}
//如果可以落脚,那么标记,并且对其进行入栈至cur_path
MarkStay(maze, cur);
SeqStackPush(cur_path, cur);
//判断是否是出口,如果是出口,比较cur_path与short_path
//比较两者size
if(isExit(maze, cur, entry)) {
printf("找到一条路径\n");
if(cur_path->size < short_path->size || short_path->size == 0) {
SeqStackAssign(cur_path, short_path);
}//这里比较两个栈内的size
SeqStackPop(cur_path);
return 1;
}
//如果不是出口则探测其四周
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);
SeqStackPop(cur_path);
return 1;
}
void GetShortPath(Maze* maze, Point entry)
{
if(maze == NULL) {
return;
}
SeqStack cur_path;//每次入栈
SeqStack short_path;//最后比较入栈,这里面存放的是最短路径
SeqStackInit(&cur_path);
SeqStackInit(&short_path);
entry.row = 0;
entry.col = 1;
_GetShortPath(maze, entry, entry, &cur_path, &short_path);
printf("最短路径是\n");
SeqStackPrintShort(&short_path);
}
再解决了这个问题后,还有一个拓展,就是倘若我们的迷宫带有环的路径,是否还能够找到最短的路径?答案肯定是不行的。接下来我们来解决一下带环的的最短路径探测。我们这里利用标记与判断落脚点进行处理,使得每次的落脚点都是前一个落脚点的加一,这样的话能够更好的处理路径,并且可以寻找最短路径。
实现如下:
void MazeInitCycle(Maze* maze)
{
if(maze == NULL) {
return;
}
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}
};
size_t i = 0;
size_t j = 0;
for(; i < ROW; ++i) {
for(j = 0; j < COL; ++j) {
maze->map[i][j] = map[i][j];
}
}
return;
}
int CanStayWithCycle(Maze* maze, Point cur, Point pre)//判断是否可以落脚
{
if(maze == NULL) {
return 0;
}
if(maze->map[cur.row][cur.col] == 1) {//如果落脚点的map值为1那么直接返回可以落脚
return 1;
}
if(maze->map[pre.row][pre.col] + 1 < maze->map[cur.row][cur.col]) {
return 1;
}
return 0;
}
void MarkStayWithCycle(Maze* maze, Point cur, Point pre)//标记为前一个点的下标+1
{
if(maze == NULL) {
return;
}
maze->map[cur.row][cur.col] = maze->map[pre.row][pre.col] + 1;
}
int _GetPathWithCycle(Maze* maze, Point cur, Point entry,
SeqStack* cur_path, SeqStack* short_path, Point pre)
{
// printf("(%d, %d)\n",cur.row, cur.col);
if(maze == NULL)
{
return 0;
}
//首先判断是否能落脚,这里判断是否能落脚时是跟前面不一样的
//需要比较落脚地方与前面地方的值的大小
if(!CanStayWithCycle(maze, cur, pre)) {
return 0;
}
//如果可以落脚,那么就对落脚点进行标记,标记方式也与前面不同
//并入栈
MarkStayWithCycle(maze, cur, pre);//标记
SeqStackPush(cur_path, cur);//入栈cur_path
pre = cur;
//如果是出口点,那么比较cur_path与short_path的size大小进行比较
if(isExit(maze, cur, entry)) {
printf("找到一条路径\n");
if(cur_path->size < short_path->size || short_path->size == 0) {
SeqStackAssign(cur_path, short_path);
}
SeqStackPop(cur_path);
return 1;
}
//如果不是出口点,那么探寻上、右、下、左四点
Point up = cur;
up.row -= 1;
_GetPathWithCycle(maze, up, entry, cur_path, short_path, pre);
Point right = cur;
right.col += 1;
_GetPathWithCycle(maze, right, entry, cur_path, short_path, pre);
Point down = cur;
down.row += 1;
_GetPathWithCycle(maze, down, entry, cur_path, short_path, pre);
Point left = cur;
left.col -= 1;
_GetPathWithCycle(maze, left, entry, cur_path, short_path, pre);
SeqStackPop(cur_path);
return 1;
}
void GetPathWithCycle(Maze* maze, Point entry)//探寻带环路径
{
if(maze == NULL) {
return;
}
SeqStack cur_path;
SeqStack short_path;
SeqStackInit(&cur_path);
SeqStackInit(&short_path);
entry.row = 0;
entry.col = 1;
_GetPathWithCycle(maze, entry, entry, &cur_path, &short_path, entry);
printf("找到一条最短路径\n");
SeqStackPrintShort(&short_path);
}
测试代码如下:
void TestInit()
{
HEAD;
Maze maze;
MazeInit(&maze);
MazeMapPrint(&maze);
}
void TestGetPath()
{
HEAD;
Maze maze;
Point entry;
MazeInit(&maze);
MazeMapPrint(&maze);
GetPathMaze(&maze, entry);
MazeMapPrint(&maze);
}
void TestGetPathByMyStack()
{
HEAD;
Maze maze;
Point entry;
MazeInit(&maze);
MazeMapPrint(&maze);
GetPathMazeByMyStack(&maze, entry);
MazeMapPrint(&maze);
}
void TestGetPathWithCycle()
{
HEAD;
Maze maze;
Point entry;
MazeInitCycle(&maze);
MazeMapPrint(&maze);
GetPathWithCycle(&maze, entry);
MazeMapPrint(&maze);
}
void TestGetShortPath()
{
HEAD;
Maze maze;
Point entry;
MazeInit(&maze);
MazeMapPrint(&maze);
GetShortPath(&maze, entry);
MazeMapPrint(&maze);
}
int main()
{
TestInit();
TestGetPath();
TestGetPathByMyStack();
TestGetShortPath();
TestGetPathWithCycle();
printf("\n");
printf("\n");
printf("\n");
printf("\n");
printf("\n");
printf("\n");
return 0;
}
欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!