迷宫求解 :1) 求简单迷宫是否存在路径 ; 2) 求多出口迷宫的最短路径 ;3) 求带环的多出口迷宫的最短路径

迷宫求解的非递归算法应用了之前的栈结构

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,"最短路径是");
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值