【算法】回溯法四步走 (6)

此题为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; } } } }

方法二:我们也可以使用二叉树,来用上我们的递归深度:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwwszw.html