棧和隊列的應用——迷宮問題(深度、廣度優先搜索)

1、迷宮問題

  給一個二維列表,表示迷宮(0表示通道,1表示圍牆)。給出算法,求一條走出迷宮的路徑。python

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表明路,圖示以下:算法

  

2、棧——深度優先搜索

  應用棧解決迷宮問題,叫作深度優先搜索(一條路走到黑),也叫作回溯法app

一、用棧解決的思路

  思路:從上一個節點開始,任意找下一個能走的點,當找不到能走的點時,退回上一個點尋找是否有其餘方向的點。spa

  使用棧存儲當前路徑。後進先出,方便回退到上一個點。blog

二、用棧代碼實現

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)
"""

  總結算法就是:建立一個空棧,首先將入口位置進棧。當棧不空時循環:獲取棧頂元素,尋找下一個可走的相鄰方塊,若是找不到可走的相鄰方塊,說明當前位置是死衚衕,進行回溯(就是講當前位置出棧,看前面的點是否還有別的出路)隊列

  使用棧來解決迷宮問題,雖然實現起來比較簡單,可是它的路徑並非最短的,極可能會繞遠,若是想走最短路徑可使用隊列來作。utf-8

3、隊列——廣度優先搜索

  應用隊列解決迷宮問題,叫作廣度優先搜索io

一、用隊列解決思路

  思路:從一個節點開始,尋找全部接下來能繼續走的點,繼續不斷尋找,直到找到出口。class

  使用隊列存儲當前正在考慮的節點。總體過程如圖所示:import

  

  建立一個空隊列,將起點1放入隊列,而後1只有一條路可走,所以1出列2進列,到3入列後因爲有兩條路可走,3出列四、5入列;隨後先走4的方向4出列6入列,再5出列7入列,此時六、7在隊列中,6又有了兩個方向,此時6出列,八、9入列,此時隊列中爲7\8\9,以此規律依次類推,直到找到出口。

  隊列中存的再也不是路徑,而是如今考慮的路,分岔的中端。所以輸出路徑會比較麻煩。

二、輸出路徑方法

  須要一個額外的列表記錄哪一個點讓哪一個點加入進來,從終點往前推導得出迷宮路徑。

三、用隊列代碼實現

# -*- 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)]
相關文章
相關標籤/搜索