回溯问题总结

回溯问题总结

个人心得

本博客大部分参考自:代码随想录 - 回溯法总结篇

问题本质

回溯是递归的副产品,常与深度优先搜索、二叉树遍历一起出现。本质是暴力搜索算法(最多剪枝优化),只不过是用递归的方式控制了for循环嵌套的数量,即:for循环横向遍历,递归纵深遍历,回溯不断调整结果集。将遍历过程抽象为树的结构可以更好地理解。

解题步骤

“Carl回溯三部曲”

  • 回溯函数backtracking模板的返回值及其参数

  • 回溯函数的终止条件

  • 回溯搜索的遍历过程

我的一些说明

1、单个集合求解时需要设置startIndex,多个集合求组和不需要

2、回溯部分的backtracking()参数startIndex,不限次数可从i开始,限制次数必须往后i + 1

3、每层遍历时主逻辑这样写:求组合时for当中int i = startIndex,否则求排列时一般从i = 0开始

4、层次去重时,要使用used数组记录元素是否被访问,出于时空复杂度的考量尽量不使用unordered_map去重,注意提前对原数组的排序问题

5、backtracking()函数的返回值一般是void,何时为bool?因为解数独找到一个符合的条件(就在树的叶子节点上)立刻 就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值,这一点在 重新安排行程、回溯算法:N皇后问题 中已经介绍过了,一样的道理。

6、注意返回条件和收集result的位置关系

  • 这里粘贴一个录友总结好的框架:

在这里插入图片描述

解题范围

回溯算法能解决如下问题:

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 棋盘问题:N皇后,解数独等等

组合问题

  • 77. 组合 - 力扣(LeetCode),给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。组合不考虑排列顺序,因此for从i = startIndex开始遍历,剪枝时,i <= n - (k - path.size()) + 1

    ​ // path.size()已经选择的元素个数

    ​ // k - path.size()还需选择的元素个数

    ​ // 在集合n中 至多 要从该起始位置 : n - (k - path.size()) + 1,开始遍历,因为再往后才开始的话集合中的元素将取不够

  • 216. 组合总和 III - 力扣(LeetCode),找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。比第一题多了总和的限制。照旧,从i = startIndex开始遍历,可以有i <= n - (k - path.size()) + 1剪枝和总和超过sum就return两种剪枝

  • 39. 组合总和 - 力扣(LeetCode),比第二题少了限制,不限制选取的次数,因此递归backtracking部分不用i + 1,直接i。剪枝只有判断sum一处。最好先对候选数组排序

  • 40. 组合总和 II - 力扣(LeetCode),难在去重。这里采用“层次去重”,最好先排序,然后引入uesd数组记录元素是否被访问过

多个集合求组和

  • 17. 电话号码的字母组合 - 力扣(LeetCode),注意这里for循环可不是从startIndex开始遍历的,因为本题每一个数字代表的是不同集合,也就是求不同集合之间的组合。而且index也不同与以往的startIndex,是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index也表示树的深度

切割问题(用求解组合的思路来解决)

有如下几个难点:

  • 切割问题其实类似组合问题
  • 如何模拟那些切割线
  • 切割问题中递归如何终止
  • 在递归循环中如何截取子串
  • 如何判断回文

子集问题

排列问题

重新安排行程

棋盘问题

性能分析

子集问题分析:

  • 时间复杂度:O(2的n次方),因为每一个元素的状态无外乎取与不取,所以时间复杂度为O(2^n)
  • 空间复杂度:O(n),递归深度为n,所以系统栈所用空间为O(n),每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为O(n)

排列问题分析:

  • 时间复杂度:O(n!),这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * … 1 = n!。
  • 空间复杂度:O(n),和子集问题同理。

组合问题分析:

  • 时间复杂度:O(2^n),组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
  • 空间复杂度:O(n),和子集问题同理。

N皇后问题分析:

  • 时间复杂度:O(n!) ,其实如果看树形图的话,直觉上是O(n^n),但皇后之间不能见面所以在搜索的过程中是有剪枝的,最差也就是O(n!),n!表示n * (n-1) * … * 1。
  • 空间复杂度:O(n),和子集问题同理。

解数独问题分析:

  • 时间复杂度:O(9^m) , m是’.'的数目。
    皇后之间不能见面所以在搜索的过程中是有剪枝的,最差也就是O(n!),n!表示n * (n-1) * … * 1。
  • 空间复杂度:O(n),和子集问题同理。

解数独问题分析:

  • 时间复杂度:O(9^m) , m是’.'的数目。
  • 空间复杂度:O(n2),递归的深度是n2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值