python 回溯法 子集树模板 系列 —— 2、迷宫问题

问题

给定一个迷宫,入口已知。问是否有路径从入口到出口,若有则输出一条这样的路径。注意移动可以从上、下、左、右、上左、上右、下左、下右八个方向进行。迷宫输入0表示可走,输入1表示墙。为方便起见,用1将迷宫围起来避免边界问题。

分析

考虑到左、右是相对的,因此修改为:北、东北、东、东南、南、西南、西、西北八个方向。在任意一格内,有8个方向可以选择,亦即8种状态可选。因此从入口格子开始,每进入一格都要遍历这8种状态。

显然,可以套用回溯法的子集树模板。

注意,解的长度是不固定的。

709432-20170529231250305-2021787932.png
图片来源:点我

代码

# 迷宫(1是墙,0是通路)
maze = [[1,1,1,1,1,1,1,1,1,1],
        [0,0,1,0,1,1,1,1,0,1],
        [1,1,0,1,0,1,1,0,1,1],
        [1,0,1,1,1,0,0,1,1,1],
        [1,1,1,0,0,1,1,0,1,1],
        [1,1,0,1,1,1,1,1,0,1],
        [1,0,1,0,0,1,1,1,1,0],
        [1,1,1,1,1,0,1,1,1,1]]

m, n = 8, 10   # 8行,10列
entry = (1,0)  # 迷宫入口
path = [entry] # 一个解(路径)
paths = []     # 一组解


# 移动的方向(顺时针8个:N, EN, E, ES, S, WS, W, WN)
directions = [(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1)]


# 冲突检测
def conflict(nx, ny):
    global m,n,maze
    
    # 是否在迷宫中,以及是否可通行
    if 0 <= nx < m and 0 <= ny < n and maze[nx][ny]==0:
        return False
    
    return True


# 套用子集树模板
def walk(x, y): # 到达(x,y)格子
    global entry,m,n,maze,path,paths,directions
    
    if (x,y) != entry and (x % (m-1) ==0 or y % (n-1) == 0):  # 出口
        #print(path)
        paths.append(path[:]) # 直接保存,未做最优化
    else:
        for d in directions: # 遍历8个方向(亦即8个状态)
            nx, ny = x+d[0], y+d[1]
            path.append((nx,ny))     # 保存,新坐标入栈
            if not conflict(nx, ny): # 剪枝
                maze[nx][ny] = 2         # 标记,已访问(奇怪,此两句只能放在if区块内!)
                walk(nx, ny)
                maze[nx][ny] = 0         # 回溯,恢复
            path.pop()               # 回溯,出栈


# 解的可视化(根据一个解x,复原迷宫路径,'2'表示通路)
def show(path):
    global maze
    
    import pprint, copy
    
    maze2 = copy.deepcopy(maze)
    
    for p in path:
        maze2[p[0]][p[1]] = 2 # 通路
        
    pprint.pprint(maze)  # 原迷宫
    print()
    pprint.pprint(maze2) # 带通路的迷宫


# 测试
walk(1,0)
print(paths[-1], '\n') # 看看最后一条路径
show(paths[-1])

效果图

709432-20170529224854883-48525773.jpg

转载于:https://www.cnblogs.com/hhh5460/p/6919320.html

<think>嗯,用户问的是DFS和回溯法的区别。这个问题我得仔细想想,不能混淆了。首先,得明确两者的定义和应用场景。DFS,也就是深度优先搜索,是一种遍历或搜索或图的算法,它沿着分支一直深入到底,再回溯到上一个节点继续探索。而回溯法则是一种通过试错来寻找问题解决方法的算法,当发现当前路径不能得到有效解时,会退回上一步重新选择。 那它们的区别到底在哪里呢?可能用户在学习算法时,对这两个概念容易混淆,尤其是在解决类似的问题,比如排列组合、迷宫问题时,两者都有应用。需要从不同的角度来区分,比如处理问题的类型、实现方式、剪枝的运用等方面。 首先,DFS主要是一种遍历方式,用于系统地访问所有节点,而回溯法则更侧重于通过递归试探和回退来寻找可行解或最优解。回溯法通常会利用DFS作为其遍历结构,但加入了剪枝等优化手段,提前排除不可能的情况,减少搜索空间。 举个例子,比如在解决八皇后问题时,使用回溯法,每次放置一个皇后后,检查是否冲突,如果冲突则不再继续往下搜索,而是回退到上一步。而DFS在这里作为遍历的可能路径的方法,但回溯法加入了条件判断,及时剪枝。 另外,DFS的应用范围更广,比如图的连通性检测、拓扑排序等,而回溯法则更多用于组合优化、决策问题,如数独、排列组合等。需要说明回溯法通常用递归实现,而DFS可以用递归或者栈来实现,但回溯法在递归返回时,会撤销之前的状态,也就是“回溯”操作。 可能用户在学习的时候,看到回溯法经常用递归和DFS的结构,所以会产生混淆。这时候需要指出回溯法是在DFS的基础上,增加了状态管理和剪枝,因此可以说回溯法是DFS的一种应用,但更强调试错和回退的过程。 需要确保回答结构清晰,先分别介绍两者的定义,然后对比不同点,最后举例说明。这样用户能逐步理解两者的区别,避免混淆。同时要注意用中文,使用正确的数学格式,比如变量用$...$包裹,独立公式单独成段用$$...$$,但用户的问题中没有涉及数学表达式,所以可能不需要用到这些。不过按照系统级指令,如果有的话要遵守。 总结下来,DFS是基础的搜索策略,而回溯法是基于DFS的一种优化算法,通过剪枝和状态回退提高效率。两者的核心区别在于目的和应用方式,回溯法更关注于解决决策问题中的试错过程,而DFS更关注于遍历或搜索整个结构。</think>DFS(深度优先搜索)和回溯法都是算法设计中常用的策略,两者有联系但也有本质区别。以下是逐步对比和分析: --- ### **1. 核心定义不同** - **DFS(深度优先搜索)** 一种**遍历或搜索/图结构的算法**,从根节点出发,尽可能深地探索分支,直到无法继续(叶子节点或已访问节点),再回溯到上一个未探索的节点继续搜索。 **目标**:系统地访问所有节点或找到一条路径。 - **回溯法(Backtracking)** 一种**解决问题算法思想**,通过递归尝试所有可能的候选解,若发现当前路径无法得到有效解,则撤销(回溯)上一步或几步的选择,重新尝试其他分支。 **目标**:寻找满足约束条件的可行解或最优解。 --- ### **2. 应用场景不同** - **DFS** 用于需**遍历或搜索完整结构**的问题,如: - 图的连通性检测 - 拓扑排序 - 二叉的前/中/后序遍历 - **回溯法** 用于需**穷举可能性并剪枝**的组合优化问题,如: - 排列组合(全排列、子集) - 八皇后问题 - 数独求解 - 迷宫路径搜索 --- ### **3. 实现方式关键区别** | **特性** | **DFS** | **回溯法** | |------------------|----------------------------------|--------------------------------| | **核心逻辑** | 单纯遍历,不主动回退 | 通过试错主动回退 | | **状态管理** | 通常不修改已访问状态 | 记录并修改路径状态(需撤销) | | **剪枝** | 不一定使用剪枝 | 必须剪枝以优化搜索空间 | | **递归终止条件** | 到达叶子节点或完成遍历 | 找到解或确认无解 | --- ### **4. 举例说明** #### **DFS示例:图的遍历** ```python def dfs(node): if node is visited: return visit(node) # 访问节点 for neighbor in node.neighbors: dfs(neighbor) # 递归深入 ``` **目标**:访问所有节点。 --- #### **回溯法示例:全排列问题** ```python def backtrack(path, choices): if 满足终止条件(如路径长度等于数组长度): 记录结果 return for choice in choices: if 选择无效(如元素已用): continue # 剪枝 做选择(将choice加入path) backtrack(path, 新choices) 撤销选择(从path移除choice) # 关键:回溯操作 ``` **目标**:生成所有排列,通过剪枝跳过无效路径。 --- ### **5. 总结区别** | **维度** | **DFS** | **回溯法** | |-------------|-----------------------------|------------------------------| | **本质** | 搜索算法 | 算法设计思想(基于DFS实现) | | **侧重点** | 遍历结构 | 通过试错和剪枝寻找解 | | **回退操作** | 自然回溯(递归栈回退) | 显式撤销选择(主动回溯) | | **优化手段** | 无剪枝 | 必须剪枝 | --- ### **关键结论** 回溯法可视为**DFS的增强版**: - 在DFS框架中增加了**状态管理**(记录和撤销选择)**剪枝**(提前终止无效分支)。 - 回溯法更关注**问题的解空间**,而DFS更关注**结构的遍历**。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值