此题为1~9九个选择,九条路,九叉树。
for (int j = 1; j <= 9; j++) { // 如果将要填入的数与前面不冲突,则填入 if (!same(a, i, j)) { a[i] = j; // 选择分支 j choose(i + 1); // 递归深度+1 } } 回溯还原现场第四步,若该节点没有找到出口,则将当前位置回溯,还原现场,重新选择
在本题中,还原现场即 重置为0(表示还未填入1~9的数字)
for (int j = 1; j <= 9; j++) { // 如果将要填入的数与前面不冲突,则填入 if (!same(a, i, j)) { a[i] = j; choose(i + 1); //递归函数的前后操作对称 //若没有找到出口,则将当前位置重置为0,回溯,还原现场 a[i] = 0; //你看看是不是与a[i]=j是对称的操作 } } 节点状态参数定义技巧注意:我们的递归方法的方法参数,都是为了表明我们所绘树形图的当前节点的状态,当然,不仅仅是递归方法的方法参数,也包括全局成员变量也可以表示当前节点状态。
以 depth 表示递归深度(f(depth) 表示第 depth 次选择)适用场景:一般我们使用这种方法来表示本次 n 叉树的深度,而本次 n 叉树的分支选择是由for ()循环中的 i 来决定的,list.add(i),即 选第 i 条分支。
操作:
递归出口:if (depth >= n)
分支选择:list.add(depth)
递归深度增加:f(depth + 1)
以 start 表示范围区间(f(left) 表示从 start 开始向右选择[start, nums.length - 1])适用场景:一般我们使用这种方法来表示不断前进的选择,即 本次选择不会考虑左边之前的选择,从start右边开始选。
相当于我们使用start把数组划分为了两个部分,第一个部分就是我们已经选择好的元素,第二部分就是我们还未选择的元素,我们需要做的就是从start开始将还未选择的元素放入左边已选择的元素中(start + 1)。
好处:这样我们可以省去用于存储数组访问情况的used[]数组,因为我们已选择和未选择已经分开了。
操作:
\([start, nums.length - 1]\):f(start)
递归出口:if (start >= length)
分支选择:for (int i = start; i < length; i++)、list.add(i)
递归深度增加:f(i)
如 面试题 08.04. 幂集
以 [x][y] 表示完整的当前节点状态(f(x, y) 当前节点状态由 [x, y] 来表示)适用场景:一般我们使用这种方法来表示树形图上的当前节点的状态,比如说 地图上的索引位置,如 横纵坐标。
操作:
递归出口:if (x < 0 || y < 0 || x >= m || y >= n || || used[x][y])越界
分支选择:list. add(nums[x][y])
下:f(x, y + 1)
右:f(x + 1, y)
上:f(x, y - 1)
左:f(x - 1, y)
如 剑指 Offer 13. 机器人的运动范围
多种回溯法解法 面试题 08.04. 幂集幂集。编写一种方法,返回某集合的所有子集。集合中不包含重复的元素。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]这里推荐一个大佬的解法:幂集解答
以 depth 表示递归深度方法一:本题所有节点均为可行解,所以不需要记录深度。
class Solution { Set<List<Integer>> res = new HashSet<>(); // 结果集合 Set<Integer> set = new TreeSet<>(); // 使用TreeSet来保证集合内元素有序,放入结果集合时方便去重 public List<List<Integer>> subsets(int[] nums) { f(nums); return new ArrayList<>(res); } // 回溯法 public void f(int[] nums) { // if (start >= nums.length - 1) { res.add(new ArrayList<>(set)); // } Integer temp = 0; for (int i = 0; i < nums.length; i++) { if (nums[i] != Integer.MAX_VALUE) { // 用过就置为无穷大 temp = nums[i]; set.add(nums[i]); nums[i] = Integer.MAX_VALUE; f(nums); set.remove(temp); nums[i] = temp; } } } }方法二:我们也可以使用二叉树,来用上我们的递归深度: