zoukankan      html  css  js  c++  java
  • 栈和队列的应用——迷宫问题(深度、广度优先搜索)

    一、迷宫问题

      给一个二维列表,表示迷宫(0表示通道,1表示围墙)。给出算法,求一条走出迷宫的路径。

    maze = [
        [1,1,1,1,1,1,1,1,1,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,0,0,1,1,0,0,1],
        [1,0,1,1,1,0,0,0,0,1],
        [1,0,0,0,1,0,0,0,0,1],
        [1,0,1,0,0,0,1,0,0,1],
        [1,0,1,1,1,0,1,1,0,1],
        [1,1,0,0,0,0,0,0,0,1],
        [1,1,1,1,1,1,1,1,1,1]
     ]
    

      1代表墙,0代表路,图示如下:

      

    二、栈——深度优先搜索

      应用栈解决迷宫问题,叫做深度优先搜索(一条路走到黑),也叫做回溯法

    1、用栈解决的思路

      思路:从上一个节点开始,任意找下一个能走的点,当找不到能走的点时,退回上一个点寻找是否有其他方向的点。

      使用栈存储当前路径。后进先出,方便回退到上一个点。

    2、用栈代码实现

    maze = [
        [1,1,1,1,1,1,1,1,1,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,0,0,1,1,0,0,1],
        [1,0,1,1,1,0,0,0,0,1],
        [1,0,0,0,1,0,0,0,0,1],
        [1,0,1,0,0,0,1,0,0,1],
        [1,0,1,1,1,0,1,1,0,1],
        [1,1,0,0,0,0,0,0,0,1],
        [1,1,1,1,1,1,1,1,1,1]
     ]
    
    # 四个移动方向
    dirs = [
        lambda x,y: (x+1, y),   # 下
        lambda x,y: (x-1, y),   # 上
        lambda x,y: (x, y-1),   # 左
        lambda x,y: (x, y+1)    # 右
    ]
    
    
    def maze_path(x1, y1, x2, y2):   # (x1,y1)代表起点;(x2,y2)代表终点
        stack = []
        stack.append((x1, y1))
        while(len(stack)>0):
            curNode = stack[-1]   # 当前的节点(栈顶)
            if curNode[0] ==x2 and curNode[1] == y2:  # 判断是否走到终点
                # 走到终点,遍历栈输出路线
                for p in stack:
                    print(p)
                return True
    
            """搜索四个方向"""
            for dir in dirs:
                nextNode = dir(curNode[0], curNode[1])
                # 如果下一个阶段能走
                if maze[nextNode[0]][nextNode[1]] == 0:
                    stack.append(nextNode)    # 将节点加入栈
                    maze[nextNode[0]][nextNode[1]] = 2   # 将走过的这个节点标记为2表示已经走过了
                    break   # 找到一个能走的点就不再遍历四个方向
            else:
                # 一个都找不到,将该位置标记并该回退
                maze[nextNode[0]][nextNode[1]] = 2
                stack.pop()
        else:
            print("没有路")
            return False
    
    maze_path(1,1,8,8)
    
    """
    (1, 1)  (2, 1)  (3, 1)  (4, 1)  (5, 1)  (5, 2)  (5, 3)  (6, 3)  (6, 4)
    (6, 5)  (7, 5)  (8, 5)  (8, 6)  (8, 7)  (8, 8)
    """
    

      总结算法就是:创建一个空栈,首先将入口位置进栈。当栈不空时循环:获取栈顶元素,寻找下一个可走的相邻方块,如果找不到可走的相邻方块,说明当前位置是死胡同,进行回溯(就是讲当前位置出栈,看前面的点是否还有别的出路)

      使用栈来解决迷宫问题,虽然实现起来比较简单,但是它的路径并不是最短的,很可能会绕远,如果想走最短路径可以使用队列来做。

    三、队列——广度优先搜索

      应用队列解决迷宫问题,叫做广度优先搜索

    1、用队列解决思路

      思路:从一个节点开始,寻找所有接下来能继续走的点,继续不断寻找,直到找到出口。

      使用队列存储当前正在考虑的节点。整体过程如图所示:

      

      创建一个空队列,将起点1放入队列,然后1只有一条路可走,因此1出列2进列,到3入列后由于有两条路可走,3出列4、5入列;随后先走4的方向4出列6入列,再5出列7入列,此时6、7在队列中,6又有了两个方向,此时6出列,8、9入列,此时队列中为789,以此规律依次类推,直到找到出口。

      队列中存的不再是路径,而是现在考虑的路,分岔的中端。因此输出路径会比较麻烦。

    2、输出路径方法

      需要一个额外的列表记录哪个点让哪个点加入进来,从终点往前推导得出迷宫路径。

    3、用队列代码实现

    # -*- coding:utf-8 -*-
    __author__ = 'Qiushi Huang'
    
    from collections import deque   # 引入队列
    
    maze = [
        [1,1,1,1,1,1,1,1,1,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,1,0,0,0,1,0,1],
        [1,0,0,0,0,1,1,0,0,1],
        [1,0,1,1,1,0,0,0,0,1],
        [1,0,0,0,1,0,0,0,0,1],
        [1,0,1,0,0,0,1,0,0,1],
        [1,0,1,1,1,0,1,1,0,1],
        [1,1,0,0,0,0,0,0,0,1],
        [1,1,1,1,1,1,1,1,1,1]
     ]
    
    # 四个移动方向
    dirs = [
        lambda x,y: (x+1, y),   # 下
        lambda x,y: (x-1, y),   # 上
        lambda x,y: (x, y-1),   # 左
        lambda x,y: (x, y+1)    # 右
    ]
    
    
    def print_r(path):
        """打印路径"""
        curNode = path[-1]    # 最后一个节点
        realpath = []         # 出去的路径
        while curNode[2] != -1:   # 判断最后一个节点的标记是否为-1,如果是-1说明是起始点,如果不是-1就继续查找
            realpath.append(curNode[0:2])   # 拿到并添加节点x,y坐标信息
            curNode = path[curNode[2]]      # 这里curNode[2]是当前节点的前一步节点的标识:path的下标,因此path[curNode[2]]拿到前一节点
    
        realpath.append(curNode[0:2])       # 在这里curNode[2] == -1,是迷宫起点,将坐标信息加入路径
    
        realpath.reverse()    # 将列表倒序,将前面生成的从后往前的列表变为从前往后
        print(realpath)
    
    
    def maze_path_queue(x1, y1, x2, y2):   # (x1,y1)代表起点;(x2,y2)代表终点
        """用队列实现迷宫问题——深度优先搜索"""
        queue = deque()   # 创建队列
        queue.append((x1, y1, -1))    # 加入起点,第三个参数是记录时谁让它来的,这里起始点设置为-1
        path = []   # 保存出队节点
        while len(queue) > 0:   # 只有队不空就一直循环
            curNode = queue.pop()  # 队首节点出队,并存为当前节点变量
            path.append(curNode)   # 添加到path列表
            if curNode[0] == x2 and curNode[1] == y2:   # 判断是否找到终点
                print_r(path)   # 如果到达终点,打印路径
                return True
    
            for dir in dirs:    # 搜索四个方向
                nextNode = dir(curNode[0], curNode[1])    # curNode[0],curNode[1]分别是当前节点x、y
                if maze[nextNode[0]][nextNode[1]] == 0:   # 如果有路可走
                    queue.append((nextNode[0], nextNode[1], len(path) - 1))   # 后续节点进队,标记谁让它来的:path最后一个元素的下标
                    maze[nextNode[0]][nextNode[1]] = 2   # 设置为2,标记为已经走过
    
        else:   # 如果队列为空(当前节点到了死路,节点删除没有新节点加入),没有路
            print("没有路")
            return False
    
    
    maze_path_queue(1,1,8,8)
    # [(1, 1), (1, 2), (2, 2), (3, 2), (3, 1), (4, 1), (5, 1), (5, 2), (5, 3), (6, 3), (6, 4), (6, 5), (5, 5), (5, 6), (5, 7), (5, 8), (6, 8), (7, 8), (8, 8)]
    

      

  • 相关阅读:
    leetcode 34 rust
    leetcode 2 rust
    leetcode 1 rust
    leetcode 20 rust
    leetcode 287 rust
    leetcode 18 rust
    lottery抽奖
    webpack
    webpack(7)-生产环境
    webpack(6)-模块热替代&tree shaking
  • 原文地址:https://www.cnblogs.com/xiugeng/p/9687354.html
Copyright © 2011-2022 走看看