回溯总结
一、回溯算法能解决如下问题:
组合问题:N个数里面按一定规则找出k个数的集合
排列问题:N个数按一定规则全排列,有几种排列方式
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
棋盘问题:N皇后,解数独等等
二、回溯法的模板:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
三、组合问题
for循环横向遍历,递归纵向遍历,回溯不断调整结果集。
startIndex:横向遍历的起始位置,防止出现重复组合
- 如果是一个集合来求组合的话,就需要startIndex
- 如果是多个集合取组合,各个集合之间相互不影响,就不用startIndex
只适用组合问题
剪枝
- 在for循环上做剪枝操作:start到末尾的差 < k(规定选定k个数)
- 递归终止条件:组合的和大于target
去重
- 树层去重:去除for循环中重复取数的情况(解集包含重复组合)
需要先对数组排序 - 树枝去重:去除递归途中出现重复取数的情况(解集里包括重复元素)
四、切割问题
五、子集问题
在树形结构中子集问题是要收集所有节点的结果,而组合问题是收集叶子节点的结果。
六、排列问题
排列是有序的,也就是说 [1,2] 和 [2,1] 是两个集合,这和之前分析的子集以及组合所不同的地方。
可以看出元素1在[1,2]中已经使用过了,但是在[2,1]中还要在使用一次1,所以处理排列问题就不用使用startIndex了。
6.1 排列问题的不同:
每层都是从0开始搜索而不是startIndex
需要used数组记录path里都放了哪些元素了
6.2 去重
6.3 全排列去重
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出: [[1,1,2], [1,2,1], [2,1,1]]
used数组
set
时间空间复杂度
子集问题分析:
时间复杂度:O(2n),因为每一个元素的状态无外乎取与不取,所以时间复杂度为O(2n)
空间复杂度: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(n2),递归的深度是n
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架