【算法】栈实现迷宫求解(C++)(详解)

简介: 【算法】栈实现迷宫求解(C++)(详解)

迷宫求解

从入口进入开始, 向不同方向试探,走到死胡同就退回。

找迷宫通路需要使用回溯法,找迷宫通路是对回溯法的一个很好的应用,实现回溯的过程用到数据结构—

回溯法:

​ 对一个包括有很多个结点,每个结点有若干个搜索分支的问题,把原问题分解为若干个子问题求解的 算法;当搜索到某个结点发现无法再继续搜索下去时,就让搜索过程回溯(回退)到该节点的前一个结点,继续 搜索该节点外的其他尚未搜索的分支;如果发现该结点无法再搜索下去,就让搜索过程回溯到这个结点的前一 结点继续这样的搜索过程;这样的搜索过程一直进行到搜索到问题的解或者搜索完了全部可搜索分支没有解存 在为止

思路&解释

二维数组作为地图。

一开始确定一个入口——需要判定入口是否合法。

先将入口位置坐标压入栈,只要栈中不为空,那么每次判断移动方向前都要判断当前位置是不是出口。然后由此坐标开始向四周判断,判断哪有路可以走,是路就开始移动(cur-当前位置),压进栈......,走到死胡同,说明四周都不能走了,开始边popStack边向四周判断,不放过来时路上的任何一个遗漏的可能出口路径,反之,找到出口直接return true。如果该迷宫没有出口,结果栈中的元素将被全部pop出来,栈空,return false;

相关细节如下代码所示

图示

在这里插入图片描述

实际探索路径中,有的"胡同没去探索"。

maze.h

#pragma once
#include<iostream>
using namespace std;


#define MAX_SIZE 128

typedef struct _Postion//地图中点的坐标,这个栈中存的元素就是点的坐标
{
    int _x;
    int _y;
}Postion;

typedef Postion DataType;


//栈的结构体
typedef struct _Stack
{
    DataType* top;    
    DataType* base;
}Stack;

//栈的初始化
bool initStack(Stack& S)
{
    S.base = new DataType[MAX_SIZE];
    if (!S.base)
    {
        return false;
    }
    S.top = S.base;
    return true;
}

//入栈
bool pushStack(Stack& S, DataType data)
{
    if (!S.base)
    {
        return false;
    }
    if (S.top - S.base == MAX_SIZE)
    {
        return false;
    }

    *(S.top) = data;
    S.top++;
    return true;
}

//出栈
bool popStack(Stack& S,DataType& e)
{
    if (S.top == S.base)
    {
        return false;
    }
    e = *(--S.top);
    return true;
}

//返回栈顶元素         
DataType* getTop(Stack& S)
{
    if (S.top - S.base == 0)
    {
        return NULL;
    }
    //注意何时自增何时不自增
    return S.top-1;//返回栈顶元素的指针
}

//返回栈中元素个数
int getSize(Stack& S)
{
    return S.top - S.base;
}

//判断栈是否为空
bool isEmpty(Stack& S)
{
    if (S.top == S.base)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//销毁栈
void destoryStack(Stack& S)
{
    if (S.base)
    {
        delete[] S.base;
        S.top = S.base = NULL;
    }
}

maze.cpp

#include<iostream>
#include<assert.h>
#include"maze.h"
using namespace std;

#define ROW  6//行
#define COL  6//列

//地图
typedef struct _Maze
{
    int map[ROW][COL];
}Maze;

//根据给出给出的地图数据初始化结构体地图
void initMaze(Maze& m, int map[ROW][COL])
{
    for (int i = 0; i < ROW; i++)
    {
        for (int j = 0; j < COL; j++)
        {
            m.map[i][j] = map[i][j];
        }
    }
}

//打印迷宫(地图)
void printMaze(Maze& m)
{
    for (int i = 0; i < ROW; i++)
    {
        for (int j = 0; j < COL; j++)
        {
            cout << m.map[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

//判断是否是有效的入口
bool isValidEnter(Maze* m,Postion enter)
{
    assert(m);//断言-里面的表达式为0直接终止程序,注意里面的内容是什么
    //只要入口在四个边界上就是合法的,并且是1(道路)
    if (((enter._x == 0 || enter._x == ROW - 1) || (enter._y == 0 || enter._y == COL - 1)))
    {
        return true;
    }
    return false;
}

//判断当前位置是否是出口
bool isVaildExit(Maze* m, Postion cur, Postion enter)
{
    assert(m);
    //该结点不能是入口点,除了入口点,在边界上就是合法出口

    if ((cur._x != enter._x || cur._y != enter._y) && ((cur._x == 0 || cur._x == ROW - 1) || (cur._y == 0 || cur._y == COL - 1)))
    {
        return true;
    }
    else
    {
        return false;
    }
}

//判断当前结点的下一个结点是否能走通-是不是可以走的点
bool isNextPass(Maze* m, Postion cur, Postion next)
{
    assert(m);
    //判断next是不是cur的下一个结点
    //同一行相邻或者同一列相邻
    if (((next._x == cur._x) && ((next._y == cur._y + 1) || (next._y == cur._y - 1)))
        || ((next._y == cur._y) && ((next._x = cur._x + 1) || (next._x = cur._x - 1))))
    {
        //确实是cur的下一个结点(相邻的 )
        //判断这个点是不是在迷宫里
        //合法坐标并且那个位置的值是1
        if (((next._x >= 0 && next._x < ROW) && (next._y >= 0 && next._y < COL)) 
            && (m->map[next._x][next._y] == 1))
            //最后的参数==1,不仅仅是看是否是可以走的位置(道路是1),
            //同时有了这个我们就不用倒着往往前走了(不走重复的路),不是有效的结点不只是墙(0)
            //走过的也不是有效结点,直接pop出栈,通过出栈来往前回退
        {
            return true;
        }
    }
    return false;
}

//寻找迷宫通路
bool PassMaze(Maze* m, Postion enter, Stack& s)
{
    assert(m && isValidEnter(m, enter));

    Postion cur = enter;//cur存储当前结点
    Postion next;//下一个结点,从入口开始出发向四周移动

    //先将入口压入栈中
    pushStack(s, cur);
    m->map[cur._x][cur._y] = 2;//将入口值改为2

    //循环求解-当栈中还有路径时
    while (!isEmpty(s))
    {
        cur = *getTop(s);//取到栈顶元素
        //判断当前位置是否是出口
        if (isVaildExit(m, cur, enter))//注意参数传递顺序
        {
            return true;//是出口直接返回
        }
        
        //不是出口继续在周围判断
        //把cur当前刚才那个位置拿过来向四周判断
        
        //先向左判断
        next = cur;
        next._y = cur._y - 1;
        if (isNextPass(m,cur,next))//如果下一个结点走得通
        {
            //走得通就走到那个位置-压进栈
            pushStack(s, next);
            //走过的位置-标记
            m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1;

            //之后
            continue;
        }
        //走不通向另一个方向判断
        //向右走一步
        next = cur;
        next._y = cur._y + 1;
        if (isNextPass(m, cur, next))
        {
            pushStack(s, next);
            m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1;
            continue;
        }

        //向下走一步
        next = cur;
        next._x = cur._x + 1;
        if (isNextPass(m, cur, next))
        {
            pushStack(s, next);
            m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1;
            continue;
        }

        //向上走一步
        next = cur;
        next._x = cur._x - 1;
        if (isNextPass(m, cur, next))
        {
            pushStack(s, next);
            m->map[next._x][next._y] = m->map[cur._x][cur._y] + 1;
            continue;
        }

        //走到这里说明此结点的四个方向都走不通
        //进行回溯
        Postion tmp;//没用 临时接收
        popStack(s, tmp);//出栈

    }
    return false;
}

int main(void)
{
    //0-墙 1-路
    int map[ROW][COL] = {
        0,0,1,0,0,0,
        0,0,1,1,1,0,
        0,0,1,0,0,0, 
        0,1,1,1,1,0,
        0,0,1,0,1,0,
        0,0,0,0,1,0 
    };


    Maze m;//创建一个迷宫(地图)
    initMaze(m, map);//初始化迷宫
    printMaze(m);//打印迷宫

    cout << "_______" << endl;

    //迷宫入口
    Postion enter;
    enter._x = 0;
    enter._y = 2;

    //定义栈
    Stack s;//用于保存走过的轨迹,便于回溯
    initStack(s);//初始化栈

    int ret = PassMaze(&m, enter, s);
    if (ret)
    {
        cout << "有解" << endl;
    }
    else
    {
        cout << "无解" << endl;
    }
    printMaze(m);


    return 0;
}

结果

在这里插入图片描述

注意

1.指针自增情况

2.参数传递顺序

3.F5是个好东西

在这里插入图片描述

相关文章
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
75 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
2月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
43 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
2月前
|
算法
数据结构与算法二:栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式
这篇文章讲解了栈的基本概念及其应用,并详细介绍了中缀表达式转换为后缀表达式的算法和实现步骤。
62 3
|
2月前
|
存储 算法 C++
高精度算法(加、减、乘、除,使用c++实现)
高精度算法(加、减、乘、除,使用c++实现)
621 0
高精度算法(加、减、乘、除,使用c++实现)
|
2月前
|
算法 定位技术
数据结构与算法学习九:学习递归。递归的经典实例:打印问题、阶乘问题、递归-迷宫问题、八皇后问题
本文详细介绍了递归的概念、重要规则、形式,并展示了递归在解决打印问题、阶乘问题、迷宫问题和八皇后问题等经典实例中的应用。
48 0
|
2月前
|
存储 算法 决策智能
【算法】博弈论(C/C++)
【算法】博弈论(C/C++)
|
2月前
|
存储 算法 C++
【算法】哈希映射(C/C++)
【算法】哈希映射(C/C++)
|
2月前
|
机器学习/深度学习 人工智能 算法
【算法】最长公共子序列(C/C++)
【算法】最长公共子序列(C/C++)
|
2月前
|
人工智能 算法 BI
一篇带你速通差分算法(C/C++)
一篇带你速通差分算法(C/C++)