回溯算法

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就 “回溯” 返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为 “回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。

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

解决一个回溯问题,实际上就是一个决策树的遍历过程。只需要思考 3 个问题:
1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。

步骤
1.画出递归树,找到状态变量(回溯函数的参数)
2.确立结束条件
3.找准选择列表
4.判断是否需要剪枝
5.作出选择,递归调用,进入下一层
6.撤销选择

回溯算法的框架

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

核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」

总结

子集、组合与排列是不同性质的概念。子集、组合是无关顺序的,而排列是和元素顺序有关的,如[1,2]和[2,1]是同一个组合(子集),但[1,2]和[2,1]是两种不一样的排列。

“排列”问题使用used数组来标识选择列表;
子集和组合类,使用start参数标记选择列表;

对于重复元素问题,可以先对选择列表排序,再进行枝剪
在子集中if(i>start&&nums[i]==nums[i-1]) 枝剪;
在排列中 if(i>0&&nums[i]==nums[i-1]&&!used[i-1]) 枝剪,i>0是因为第一个元素前面没有重复,再判断上一个相同的元素是否被使用,如果没使用说明回溯到同一层,不能再使用要枝剪,如果使用过说明不在同一层可以使用。

排列
leetcode 46.全排列
leetcode 47. 全排列 II

子集,组合
leetcode 78.子集
leetcode 90.子集 II
leetcode 77. 组合
leetcode 39. 组合总和
leetcode 40. 组合总和 II

leetcode 22. 括号生成
leetcode 401. 二进制手表

搜索
leetcode 37. 解数独
leetcode 51. N皇后
leetcode 79.单词搜索
leetcode 17.电话号码的字母组合
leetcode 131. 分割回文串

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值