迷宫---多出口迷宫的最短路径

如图是一个有三个出口的迷宫,现在来求它的最短路径(不带环)
这里写图片描述
思想:定义两个栈,一个栈存放当前路径。一个栈存最短路径,每次都将当前路径和最短路径比较如果当前路径小于最短路径,那就交换两个栈中的元素
代码实现:

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);
}

运行结果
这里写图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章