题目描述
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2] 输出: [[1,1,2],[1,2,1],[2,1,1]]
示例 2:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
思路解析
这是一道很典型的可以使用回溯算法求解的题。
回溯算法是一种通过尝试所有可能的解决方案来找到问题的所有解的算法。对于全排列问题,我们可以使用回溯算法来生成所有可能的排列。在处理包含重复数字的序列时,为了避免生成重复的排列,我们还需要进行一些额外的处理。
以下是具体的思路步骤:
- 排序:首先对输入的数组
nums
进行排序,这样相同的数字会相邻排列,方便后续去重。 - 回溯过程:
- 使用一个布尔数组
used
来标记每个数字是否已经在当前排列中使用过。 - 从一个空排列开始,逐步尝试将未使用的数字添加到排列中。
- 在添加数字时,需要检查当前数字是否和前一个数字相同,并且前一个数字未被使用。如果是这种情况,说明添加当前数字会导致重复的排列,应该跳过。
- 使用一个布尔数组
- 终止条件:当排列的长度等于数组的长度时,说明已经生成了一个完整的排列,将其添加到结果列表中。
- 回溯操作:在递归调用返回后,需要撤销上一步的选择,即将当前数字标记为未使用,以便尝试其他可能的排列。
代码实现
class Solution {public List<List<Integer>> permuteUnique(int[] nums) {List<List<Integer>> result = new ArrayList<>();if (nums == null || nums.length == 0) {return result;}Arrays.sort(nums);boolean[] used = new boolean[nums.length];backtrack(nums, used, new ArrayList<>(), result);return result;}public void backtrack(int[] nums, boolean[] used, List<Integer> path, List<List<Integer>> result) {// 终止条件:当排列的长度等于数组的长度时,说明已经生成了一个完整的排列if (path.size() == nums.length) {result.add(new ArrayList<>(path));return;}for (int i = 0; i < nums.length; i++) {if (used[i]) {continue;}// 去重逻辑:如果当前数字和前一个数字相同,并且前一个数字未被使用,跳过if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {continue;}used[i] = true;path.add(nums[i]);backtrack(nums, used, path, result);// 回溯操作:撤销上一步的选择used[i] = false;path.remove(path.size() - 1);}}
}