总体技巧
一般需要在回溯函数开头确定退出逻辑,当搜索的字符串达到指定长度就退出,同时在回溯的时候需要带上索引位置index,一般从0位置开始回溯,对于没有得到完整答案的序列(临时结果)也要在回溯中传递。回溯问题可以分为以下几类。
子集问题
子集问题要求找出给定数组的所有子集。在解法上,可以使用回溯算法,并通过一个start参数来排除已经选择的数字,以避免重复选择。具体步骤如下:
- 初始化:创建一个空的结果列表
res和一个空的临时列表track来记录当前的子集。 - 回溯函数:在回溯函数中,首先将当前的
track添加到res中,表示找到了一个新的子集。然后,从start索引开始遍历数组,对于每个未选择的数字,将其添加到track中,并递归调用回溯函数继续搜索。递归调用返回后,需要撤销选择,即将刚刚添加的数字从track中移除,以便进行下一个选择的尝试。 - 终止条件:当回溯函数遍历完数组中的所有数字时,搜索过程结束。
此外,子集问题还可以使用位运算或递归的思想来解决,但回溯算法是其中较为直观和通用的一种方法。
排列问题
排列问题要求找出给定数组的所有排列。与子集问题类似,排列问题也可以使用回溯算法来解决。但关键在于,排列需要考虑元素的顺序,因此需要使用一个used数组或类似的数据结构来记录哪些元素已经被选择过,以避免重复排列。具体步骤如下:
- 初始化:创建一个空的结果列表
res和一个空的临时列表path来记录当前的排列,以及一个used数组来记录元素的选择状态。 - 回溯函数:在回溯函数中,首先检查
path的长度是否等于数组的长度,如果是,则表示找到了一个新的排列,将其添加到res中。然后,遍历数组中的每个元素,如果某个元素尚未被选择(即used[i]为false),则选择该元素(将其添加到path中,并将used[i]设置为true),递归调用回溯函数继续搜索。递归调用返回后,需要撤销选择(即将path中的最后一个元素移除,并将used[i]设置为false)。 - 终止条件:当回溯函数遍历完数组中的所有元素,并且每个元素都被选择过一次时,搜索过程结束。
组合问题
组合问题要求找出给定数组中满足特定条件的所有组合(如长度为k的组合)。组合与排列的区别在于,组合不考虑元素的顺序。因此,在解法上,组合问题也可以使用回溯算法,但需要注意去重和剪枝。具体步骤如下:
- 初始化:创建一个空的结果列表
res和一个空的临时列表path来记录当前的组合。 - 回溯函数:在回溯函数中,首先检查
path的长度是否等于指定的组合长度k,如果是,则表示找到了一个新的组合,将其添加到res中。然后,从某个起始索引(通常是当前层数的索引加1,以避免重复选择)开始遍历数组,对于每个未选择的数字,将其添加到path中,并递归调用回溯函数继续搜索。递归调用返回后,需要撤销选择。 - 去重和剪枝:在遍历过程中,可以使用一些策略来避免重复组合和提前终止不可能产生解的分支。例如,可以对数组进行排序,然后在回溯过程中跳过相同的元素;或者使用一个
start参数来限制搜索的起始位置。 - 终止条件:当回溯函数遍历完数组中的所有数字,或者
path的长度达到指定的组合长度k时,搜索过程结束。
总结
子集、排列、组合类型的回溯题目解法在核心思想上都是基于回溯算法,但在具体实现上存在一些区别。子集问题主要关注如何避免重复选择数字;排列问题需要考虑元素的顺序,并使用used数组来记录选择状态;组合问题则需要关注去重和剪枝策略,以确保找到所有满足条件的唯一组合。
具体题目
17 电话号码的数字组合
- 需要构建record数组
77 组合给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
- 标记开始位置为0,不断回溯
46 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
- 在回溯过程中,交换trace[i] trace[first] 的位置,保持数组分为填过和未填过两个部分
39 组合总和
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
- target 为 0 把数据加入
- 因为可以重复使用数据,每次回溯从0开始
22 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
- 根据括号的数量判断退出逻辑, 左括号数量 永远 < 右括号数量,等于且等于total的时候,返回一个结果
- 一次回溯需要先加左括号,再加右括号
79 单词搜索
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
- 需要visited数组辅助遍历过的格子
- 需要判断访问格子的有效性
- 四个方向有一个能回溯正确就返回结果
52 N皇后II
n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
- 以一个纬度遍历,判断另外三个纬度是否满足要求, column 和 row-i 和 row+i
子集:需要注意索引顺序,不能重复使用
- 如果使用选或者不选的方法,需要将index遍历到最后,再将path加入到res中
- 如果使用遍历选取元素的方法,不用考虑index位置,直接将path加入到res中
排列:只需要注意是否被遍历过
组合:需要注意索引顺序和是否被遍历过