力扣46:全排列
题目分析
这道题属于上一篇——“回溯算法解题框架与思路”中的 “元素不重复不可复用” 那一类中的 排列类问题。
我们来回顾一下当时是怎么说的:
排列和组合的区别在于,排列对“顺序”有要求。比如 [1,2] 和 [2,1] 是两个不同的结果。
这就导致了同一个元素 在同一条路径中不可重复使用,在不同的路径中可以重复使用。
比如数组 [1,2,3] ,在第一条路径 [1,2,3]中,1只能出现一次。但是在另一条选择路径 [2,1,3] 中,1或者在其他路径中出现过的元素仍然可以继续使用。
为了保证 “同一元素在一条路径中只能使用一次” ,我们需要额外维护一个boolean数组用于记录元素的使用情况:已经使用过的元素标记为 “true” ,没有使用过的元素标记为 “false”;
套用框架:
class Solution {//用于存放子结果的容器List<List<Integer>> res = new LinkedList<>();public List<List<Integer>> permute(int[] nums) {//存放路径的容器LinkedList<Integer> track = new LinkedList<>();//用于记录元素使用情况的boolean数组//数组初始值全为false,意为“该元素尚未使用”boolean[] used = new boolean[nums.length];backtrack(nums,track,used);return res;}//backtrack函数参数选用:函数参数设置时只需要看看这个函数想达到目的//需要那些参数,然后把这些参数全丢尽括号中就好了void backtrack(int[] nums,LinkedList<Integer> track,boolean[] used){//终止条件:当子集收集满后放入最终解集中if(track.size() == nums.length){res.add(new LinkedList(track));return;}//遍历选择列表for(int i = 0;i < nums.length;i++){//如果这个元素已经使用过了,就跳过这个选择if(used[i]){continue;}//做选择track.add(nums[i]);//更新元素使用情况——“已使用”used[i] = true;//进入下一层决策backtrack(nums,track,used);//撤销选择track.removeLast();//更新元素使用情况——“未使用”used[i] = false;}}
}
总结
在排列问题中,为了防止出现重复结果,我们会维护一个boolean数组来记录元素的使用情况。
与 组合/子集 类问题相比,排列类问题在进入下一层决策时并没有将起始元素下标后移一位。这样就不会影响到选择列表。
比如对于数组[1,2,3],假如一条路径是从 2 开始的,那么进入下一层决策树时,选择列表仍然是 [1,2,3]。
而对比与 组合/子集 类问题,此时他们的选择列表就只有 [3] 了。