LCR 084. 全排列 II
解题思路
-
排序数组: 通过 Arrays.sort(nums) 将输入数组 nums 进行排序,这有助于确保相同的元素在一起,从而方便后续剪枝操作。
-
初始化变量: 创建一个存储全排列结果的二维列表 res,一个用于暂存当前排列的列表 track,以及一个布尔数组 used 用于记录元素的使用情况。
-
主函数: permuteUnique 是主函数,调用时传入待排列的数组 nums。在函数内部,首先对数组进行排序,然后初始化 used 数组,并调用 backtrack 函数开始回溯。
-
回溯函数: backtrack 是递归的回溯函数,负责生成全排列。
-
叶子节点处理: 当 track 的长度等于数组长度时,即找到一个全排列,将其添加到结果列表 res 中。
-
元素选择: 使用 for 循环遍历数组中的元素,根据一定条件进行选择或剪枝。
-
排除已选择的元素: 如果 used[i] 为 true,说明该元素已经被选择,直接跳过。
-
剪枝操作: 利用排序后的数组,当当前元素与前一个元素相同,并且前一个元素未被使用时,跳过当前元素,以避免生成重复排列。
-
选择当前元素: 将当前元素加入 track 列表,标记为已使用,然后递归调用 backtrack。
-
撤销选择: 在递归完成后,撤销选择,即将 track 列表的最后一个元素移除,同时标记当前元素为未使用。
-
class Solution {
List<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);// 先排序 让相同元素在一起
used = new boolean[nums.length];
backtrack(nums);
return res;
}
void backtrack(int[] nums){
// 收集叶子节点
if(track.size() == nums.length){
// 全排列 树的高度是数组长度
res.add(new LinkedList<>(track));
return;
}
for(int i = 0; i < nums.length; i++){
// 排除已经选过的
if(used[i] == true){
continue;
}
// 也就是元素虽然相同 但是没被使用过
if(i > 0 && nums[i] == nums[i - 1] && !used[i - 1]){
continue;
}
track.add(nums[i]);
used[i] = true;
backtrack(nums);
// 撤销选择
track.removeLast();
used[i] = false;
}
}
}