Given a collection of numbers, nums
, that might contain duplicates, return all possible unique permutations in any order.
Example 1:
Input: nums = [1,1,2] Output: [[1,1,2], [1,2,1], [2,1,1]]
Example 2:
Input: nums = [1,2,3] Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
Constraints:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
全排列II。
一刷的时候直接抄答案,但是依然记不住,二刷终于搞懂了,来写个题解。这个题跟版本一相比只是多了一个条件,就是 input 数组里面是有重复元素的,但是得出的 permutation 里面不能有重复。
思路依然是 backtracking,同时因为需要去掉重复元素带来的困扰,我们需要用一个 visited 数组来记录数字是否被用过。这里我参考了LC中文网一个大神的解释,讲的非常好,尤其帮助我理解了回溯的具体过程和怎么去重的部分。回溯的 helper 函数的主要部分还是跟其他回溯类型的题差不多,但是这个题需要先对 input 排序(帮助剪枝)和额外的 visited 数组记录。
时间O(N * N!)
空间O(N * N!)
Java实现
18行判断是否跳过当前数字,是如下两个原则
- 如果当前数字已经visited过了,自然就跳过
- 如果当前数字跟之前一个数字相同且之前一个数字是没有被visited过的(实际是回溯的时候又被标记成false的),跳过当前这个数字
- 举例,比如[1a, 1b, 2a],你要确保1a一定要在1b之前,否则就会有重复的解。避免这个问题的办法(可以看一下loickenleetcode的评论)就是当你发现1b == 1a同时1a还没有被visited过的时候,你就必须要跳过1b
1 class Solution { 2 public List<List<Integer>> permuteUnique(int[] nums) { 3 List<List<Integer>> res = new ArrayList<>(); 4 if (nums == null || nums.length == 0) { 5 return res; 6 } 7 Arrays.sort(nums); 8 helper(res, new ArrayList<>(), nums, new boolean[nums.length]); 9 return res; 10 } 11 12 private void helper(List<List<Integer>> res, List<Integer> list, int[] nums, boolean[] visited) { 13 if (list.size() == nums.length) { 14 res.add(new ArrayList<>(list)); 15 return; 16 } 17 for (int i = 0; i < nums.length; i++) { 18 if (visited[i] || i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) { 19 continue; 20 } 21 visited[i] = true; 22 list.add(nums[i]); 23 helper(res, list, nums, visited); 24 visited[i] = false; 25 list.remove(list.size() - 1); 26 } 27 } 28 }