每日一题20250206-LC47

题目信息

目难度:medium 掌握程度:🌟🌟🌟

47. 全排列 II - 力扣(LeetCode)

给定一个可包含重复数字的序列 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]]

提示:

1 <= nums.length <= 8 -10 <= nums[i] <= 10

解题思路

回溯,在全排列的基础上加了两个扩展,

  1. 输入数字有重复
  2. 结果需要不重复

需要想办法去重,有两个策略,直接暴力回溯,然后对结果直接进行一次去重即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {

    private Set<List<Integer>> res;

    private List<Integer> path;

    private boolean[] visited;

    public List<List<Integer>> permuteUnique(int[] nums) {
        res = new HashSet<>();
        path = new ArrayList<>();
        visited = new boolean[nums.length];
        backtrack(nums,nums.length);
        return new ArrayList<>(res);
    }

    private void backtrack(int[] nums,int n){
        if(path.size() == n){
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i = 0 ; i < n ; i++){
            if(!visited[i]){
                path.add(nums[i]);
                visited[i] = true;
                backtrack(nums,n);
                visited[i] = false;
                path.remove(path.size()-1);
            }


        }
    }
}

优化

在不用 set 的情况下,如何去重

先看看是如何重复的 比如 [1,1,2], 重复的话,就会出现两个 [1,1,2]

考虑重复元素一定要 优先排序 ,将重复的都放在一起,便于找到重复元素和剪枝!!!

当前元素和前一个元素值相同(此处隐含这个元素的 index>0 ),并且前一个元素还没有被使用过的时候,我们要剪枝

1
2
3
if(visited[i] || (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) ){
                continue;
}

最后答案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution {

    private List<List<Integer>> res;

    private List<Integer> path;

    private boolean[] visited;

    public List<List<Integer>> permuteUnique(int[] nums) {
        res = new ArrayList<>();
        path = new ArrayList<>();
        visited = new boolean[nums.length];
        Arrays.sort(nums);
        backtrack(nums,nums.length);
        return res;
    }

    private void backtrack(int[] nums,int n){
        if(path.size() == n){
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i = 0 ; i < n ; i++){
            if(visited[i] || (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) ){
                continue;
            }
                path.add(nums[i]);
                visited[i] = true;
                backtrack(nums,n);
                visited[i] = false;
                path.remove(path.size()-1);

        }
    }
}

相关题目

回溯排列的基础题目

46. 全排列 - 力扣(LeetCode)

回溯组合的基础题目 77. 组合 - 力扣(LeetCode)

排列与组合的主要差异在于

排列需要维护一个 visited[] 数组来标记当前元素是否已选择,组合需要在回溯过程中,控制选择区间

updatedupdated2025-02-062025-02-06