力扣第四十七题-全排列 II

301 阅读3分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

前言

力扣第四十七题 全排列 II 如下所示:

给定一个可包含重复数字的序列 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 = [1,1,2],首次递归已经选择过了第一个元素 nums[0]。等到再次回溯回来进行下一次迭代时,就不应该再选择 nums[1] = 1 了,这样就能防止结果重复

举个例子

此处以示例 1中的 nums = [2,1,1] 作为例子

  1. 先将数组 升序nums = {1, 1, 2}
  2. i=0nums[0] = 1 入栈,栈中的值为 1,并继续下一层递归
  3. 递归1:因 i = 0 已选择过,跳过
  4. 递归1:i=1nums[1] = 1 入栈,栈中的值为 1 -> 1,并继续下一层递归
  5. 递归2:因 i = 0i = 1 已选择过,跳过
  6. 递归2:i=2nums[2] = 2 入栈,栈中的值为 1 -> 1 -> 2,此时 栈满,记录这一组结果,并将栈顶出栈,栈中的值为 1 -> 1。向上回溯
  7. 递归1:先将栈顶出栈,栈中的值为 1i=2nums[2] = 2 入栈,栈中的值为 1 -> 2,并继续下一层递归
  8. 递归2:因 i = 0i = 2 已选择过,跳过
  9. 递归2:i=1nums[1] = 1 入栈,栈中的值为 1 -> 2 -> 1,此时 栈满,记录这一组结果,并将栈顶出栈,栈中的值为 1 -> 2。向上回溯
  10. 递归1:先将栈顶出栈,栈中的值为 1。当前层遍历结束向上回溯
  11. 先将栈顶出栈,栈为空。因 i=1 时,nums[1] == nums[0],故跳过。(同层相邻元素相等)
  12. ......,后续过程基本一致
  13. 最终可以获得正确的结果集:[[1,1,2],[1,2,1],[2,1,1]]

二、实现

实现代码

实现代码与思路保持一致,其中使用 HashMap 来保存路径中的下标

    /**
     * 递归
     */
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> ret = new ArrayList<>();
        Stack<Integer> stack = new Stack<>();
        Arrays.sort(nums);  // 升序排列
        dfs(ret, nums, stack, new HashMap<>());
        return ret;
    }

    public void dfs(List<List<Integer>> list, int[] nums, Stack<Integer> stack, Map<Integer, Object> map) {
        if (stack.size() == nums.length) {
            list.add(new ArrayList<>(stack));
            return;
        }
        for (int i=0; i<nums.length; i++) {
            int num = nums[i];
            if (map.containsKey(i) || (i !=0 && num == nums[i-1] && map.containsKey(i-1)))
                continue;
            stack.push(num);
            map.put(i, num);
            dfs(list, nums, stack, map);
            stack.pop();
            map.remove(i);
        }
    }

测试代码

public static void main(String[] args) {
    int[] nums = {2, 1, 1};
    new Number47().permuteUnique(nums);
}

结果

image.png

优化版本

实现代码

使用全部变量 布尔数组 来替换 HashMap,可以有效的提升效率

    boolean[] vis;

    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        List<Integer> perm = new ArrayList<Integer>();
        vis = new boolean[nums.length];
        Arrays.sort(nums);
        backtrack(nums, ans, 0, perm);
        return ans;
    }

    public void backtrack(int[] nums, List<List<Integer>> ans, int idx, List<Integer> perm) {
        if (idx == nums.length) {
            ans.add(new ArrayList<Integer>(perm));
            return;
        }
        for (int i = 0; i < nums.length; ++i) {
            if (vis[i] || (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1])) {
                continue;
            }
            perm.add(nums[i]);
            vis[i] = true;
            backtrack(nums, ans, idx + 1, perm);
            vis[i] = false;
            perm.remove(idx);
        }
    }

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥