求解多出口迷宫最短路径的问题

求解多出口迷宫的最短路径,是在基于求解简单迷宫是否存在路径的问题的基础上的一个提高。我们首先需要认识到的是,不论是求解简单迷宫问题,还是复杂迷宫的问题,我们都需要基于栈,使用回溯法来解决问题。

  • 首先,我们先来定义一个多出口的迷宫:

                       

图中0表示墙,即无法落脚;1表示可以落脚;之后我们会用2来标记走过的路。

我们默认出口为四个边界上的点。若入口也为边界点,则出口与入口不能重合。

maze.h:
#pragma once
#include <stdio.h> 
#include<stdlib.h>
#include<stddef.h>

#define MAX_ROW 6
#define MAX_COL 6

typedef struct Point{
    int row;
    int col;
}Point;
typedef Point SeqStackType;

typedef struct SeqStack{
    SeqStackType *data;
    size_t size;
    size_t capacity;
}SeqStack;
typedef struct Maze{
    int map[MAX_ROW][MAX_COL];
}Maze;
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,1},
    {1,1,1,0,0,0},
    {0,0,1,0,0,0},
    {0,0,1,0,0,0}
};

void MazeInitShortPath(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 SeqStackDebugPrint(SeqStack* stack,const char* msg){//走过的路径中每一个点的打印
    printf("%s\n",msg);
    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");
        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(maze == 0)
        return 0;
    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 SeqStackAssgin(SeqStack* from,SeqStack* to){//将from中的数据全部拷贝至to中
    //释放to中的原有内存
    SeqStackDestroy(to);
    //根据from中的元素个数确定内存申请的大小,给to重新申请一个足够的内存
    to->size = from->size;
    to->capacity = from->capacity;
    to->data = (SeqStackType*)malloc(to->capacity * sizeof(SeqStackType));
    //再进行数据拷贝
    size_t i = 0;
    for(;i < from->size;i++){
        to->data[i] = from->data[i];
    }
}

void _GetShortPath(Maze* maze,Point cur,Point entry,SeqStack* cur_path,SeqStack* short_path){//GetShortPath的辅助函数
    printf("cur:(%d,%d)\n",cur.row,cur.col);
    //1.判断当前点能否落脚
    if(!CanStay(maze,cur)){
        return;
    }
    //2.若能落脚,给当前位置做一个标记
    //同时将当前点插入到cur_path
    Mark(maze,cur);
    SeqStackPush(cur_path,cur);
    //3.若当前点为出口,说明找到了一条出口
    if(IsExit(maze,cur,entry)){
        printf("找到了一条路径\n");
        if(cur_path->size < short_path->size || short_path->size == 0){
            //将当前路径与short_path中的路径对比,若当前路径比short_path短或short_path本身为空栈,则用当前路径替换short_path
            SeqStackAssgin(cur_path,short_path);
            printf("找到了一条相对较短的路径\n");
        }
        //若当前路径没有比short_path短,就尝试找其他路径
        SeqStackPop(cur_path);
        return;
    }
    //4.若当前点不是出口,则按顺时针方向探测四个相邻的点,递归式调用函数自身,递归式更新cur节点
    //(每次递归时,cur都是下一次要走的点,这个点能否落脚,交给递归函数作判断)
    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;
}

void GetShortPath(Maze* maze,Point entry){
    SeqStack cur_path;
    SeqStack short_path;
    SeqStackInit(&cur_path);
    SeqStackInit(&short_path);
    _GetShortPath(maze,entry,entry,&cur_path,&short_path);
    SeqStackDebugPrint(&short_path,"最短路径为");
    return;
}

通过代码我们可以认识到,由于多出口迷宫存在着多条路径,所以我们需要定义两个栈,一个栈中存放着当前走过的路径的数据,另一个栈中存放着最短路径的数据。每找到一条路径,就将当前路径的长度与最短路径的长度作比较,若存放最短路径的栈为空栈,或者当前路径的长度小于最短路径的长度,就用存放着当前路径的栈中的数据替代存放最短路径的栈中的数据。当所有路径都找到以后,此时存放最短路径的栈中的数据即可表示该迷宫的最短路径。


结果演示:

          

阅读更多
个人分类: 数据结构
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭