算法之【回溯算法】详解(python)

回溯算法

定义

回溯算法实际上**基于DFS(深度优先搜索)**的一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回到上一个状态,尝试其他的路径,这种走不通就退回再走的技术为回溯法;满足回溯条件的某个状态的点称为“回溯点”。

回溯相关问题

  1. DFS 和回溯算法区别

DFS 是一个劲的往某一个方向搜索,直到到达最底层,而回溯算法建立在 DFS 基础之上的,但不同的是在搜索过程中,达到结束条件后,恢复状态,回溯上一层,再次搜索。因此回溯算法与 DFS 的区别就是有无状态重置

  1. 何时使用回溯算法

当问题碰到走不通的路径,需要"回头",以此来查找出所有的解的时候,使用回溯算法。即满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止。

  1. 回溯算法的基本步骤
  • 找到状态变量(回溯函数的参数)
  • 依据题意定义递归结束条件
  • 找准选择列表(与函数参数相关),与第一步紧密关联
  • 判断是否需要剪枝,即提前将不符合条件的路径排除掉
  • 作出选择,递归调用,进入下一层
  • 撤销选择
  1. 回溯算法类的题型有哪些
  • 子集、组合类问题
  • 排列类问题
  • 搜索、N皇后类问题、

注意:子集、组合是无关顺序的,而排列是和元素顺序有关的,如 [1,2] 和 [2,1] 是同一个组合(子集),但 [1,2] 和 [2,1] 是两种不一样的排列!!!!

回溯算法的通用模板

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

题目举例

Leetcode78.子集

问题描述

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

**说明:**解集不能包含重复的子集。

示例

输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]

python代码题解

套用上述回溯算法的模板,path表示已选择的路径,for i in range(start, len(nums))表示当前能够选择的列表元素,注意对于组合类问题不能够选择前面已经选择过的元素,因为会存在重复结果,因此必须有一个start参数来控制每一轮能够选择的元素[start, len(nums)]

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        def backtrack(nums, path, start):
            # 将path添加到res结果中
            res.append(path.copy())
            # 当前能够选择的参数列表
            for i in range(start, len(nums)):
                # 做选择
                path.append(nums[i])
                backtrack(nums, path, i+1)
                # 撤销选择
                path.pop()
        res = []
        backtrack(nums, [], 0)
        return res
Leetcode77.组合

问题描述

给定两个整数 nk,返回 1 … n 中所有可能的 k 个数的组合。

示例

输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

python代码题解

本题与上一题基本相同,都是属于组合类问题,只是递归的终止条件不同,本题的终止条件是当路径长度为k时len(track) == k,将结果添加到res中。

套用上述回溯算法的模板,track表示已选择的路径,for i in range(start, n+1)表示当前能够选择的列表元素,注意start是从1开始的,应为题目中指明能够选择的数为1...n

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        # 数的选择范围在1-n
        def backtrack(n,k,start,track):
            if len(track) == k:
                res.append(track.copy())
                return
            # 注意i从start开始递增
            for i in range(start, n+1):
                # 做选择
                track.append(i)
                backtrack(n,k,i+1,track)
                # 撤销选择
                track.pop()
        res = []
        track = []
        backtrack(n,k,1,track)
        return res

通过上述讲解,读者应该对回溯算法的概念以及模板套路有一个基本的认识,回溯的关键在于选择与撤销选择的过程,读者可以仔细体会一下 ,相信一定会有所收获。后续会继续更新关于回溯算法的相关题解,欢迎持续关注!

如果喜欢作者,欢迎点赞、收藏及关注,谢谢!

欢迎扫描下面二维码关注公众号:阿旭算法与机器学习, 和作者共同学习交流。
在这里插入图片描述

  • 7
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
算法的基本思想是通过穷举所有可能的情况来求解问题。在回溯算法中,我们从问题的起始点开始,逐步做出选择,并根据每个选择的结果进行进一步的选择。如果某个选择导致了不可行的解决方案,我们就返回上一步并尝试其他的选择。这个过程一直持续到找到一个可行的解决方案或者所有的选择都已经尝试完毕。 在python中,回溯算法的实现通常使用递归的方式。我们可以定义一个回溯函数,该函数会接收当前的状态以及已经做出的选择。在每一步中,我们可以通过判断当前状态是否满足问题的约束条件来决定是否进行进一步的选择。如果满足约束条件,我们可以将该选择添加到解集中,并继续递归调用回溯函数。 #### 引用[.reference_title] - *1* *2* [回溯算法详解python)](https://blog.csdn.net/qq_45139385/article/details/106721207)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [python 回溯算法总结](https://blog.csdn.net/weixin_45548695/article/details/124146238)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿_旭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值