2.6 数据结构 迷宫问题

数据结构子目录https://www.jianshu.com/p/a344fa483655

有一个二维数组,表示迷宫(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]
 ]

栈的解法

思路

在任意节点,我们可以进行当前节点的上下左右四个方向的试探。

    lambda x, y: (x+1, y), #下
    lambda x, y: (x-1, y), #上
    lambda x, y: (x, y-1), #左
    lambda x, y: (x, y+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]
 ]

#移动
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 solve_maze(x1,y1,x2,y2):
    # 空栈,用来存储正确的道路
    stack = []
    # 把初始位置放入,开始行走
    stack.append((x1,y1))
    maze[x1][y1] = 2  # 2表示已经走过的路
    # 通过判断栈的长度来决定是否进行尝试
    while len(stack) > 0:
        # 取出栈顶
        cur_node = stack[-1]
        if cur_node == (x2,y2): #如果到终点了,输出栈的内容,返回True
            print(stack)
            return True
        # 遍历 移动 列表, 找到合适的移动,同时将已经走过的路添加到栈内,标注为 2 
        for i in dirs:
            next_node = i(*cur_node)
            # 看看当前节点的上下左右操作之后,那个方向可以前进。
            if maze[next_node[0]][next_node[1]] == 0:
                stack.append(next_node)
                maze[next_node[0]][next_node[1]] = 2
                break
        # 如果是死胡同,就去栈顶,重新选择道路。
        else:
            stack.pop()
    else:
        # 如果栈空了,就打印没有结果,返回false
        print("没有结果")
        return False

solve_maze(1,1,8,8)

队列的解法

思路

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

从一个节点开始,寻找所有接下来能继续走的点,继续不断寻找,直到找到出口。使用队列存储当前正在考虑的节点。

示意图

解法

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

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

代码

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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容