47. 全排列 II

41 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情

每日刷题 2022.10.13

题目

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

解题思路

  • 剑指 Offer II 083. 没有重复元素集合的全排列思路有些类似
  • 只不过在DFS处理全排列的过程中(for循环内)需要用一个Set集合来存放已经交换过的重复元素,即不让当前的nums[i]与一个等值的已经交换过的元素再进行交换;
  • 例如:对[2,1,1]操作,只会将2与第一个1进行交换,不会与第二个1交换。因为第一个元素与第二个元素交换后变成[1,2,1],进入下一层递归会得到[1,2,1][1,1,2]两种结果,加上原本的排列共有三种,刚好符合答案。
  • unordered_set也可以,但会消耗更多内存,因为散列表为了实现 O(1) 时间复杂度的查找,会开得比所需空间要大,而set底层是红黑树,需要多大的空间就占用多大,不过查找效率是 O(logN)

代码

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permuteUnique = function(nums) {
  let ans = [], chosen = [], n = nums.length, map = new Map();
  for(let i = 0; i < n; i++) {
    map.set(nums[i], (map.get(nums[i]) || 0) + 1);
  }
  function dfs (idx, nums) {
    if(idx == n){
      // 去重
      ans.push([...chosen]);
      return;
    }
    // 单层的逻辑
    let set = new Set();
    for(let i = 0; i < n; i++) {
      if(map.get(nums[i]) != 0 && !set.has(nums[i])){
        // 如果当前的元素可以放在当前的位置上
        // 那么就执行操作
        set.add(nums[i])
        chosen.push(nums[i]);
        map.set(nums[i], map.get(nums[i]) - 1);
        console.log('set',i,set)
        dfs(idx + 1, nums);
        map.set(nums[i], map.get(nums[i]) + 1)
        chosen.pop();
      }
    }
  }
  dfs(0, nums);
  return ans;
};