力扣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] 了。