LeetCode 47 Permutations II (Tag:Array Difficulty:Medium)

399 阅读3分钟

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

前言

关于 LeetCode 数组类型题目的相关解法,可见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]]

链接:leetcode-cn.com/problems/pe…

题解

  1. 构建Set. 本题目和LeetCode 46 Permutations的区别在于, 之前的题目数组中不含重复数字, 而本题目中含有重复数字, 如果还按照46题的解法, 就会出现重复的全排列, 因此本题的关键是如何在上一题的解法之上, 增加去重的操作. 那么, 首先可以想到的就是采用 JS 中的数据结构 Set, Set 集合类型保证了里面的元素是唯一的, 但是要注意的点是, 如果是数组, Set 并不能去重, 它会以为 [1,1,2] 和 [1,1,2] 这两个数组是不同的元素, 因此我们可以先转换为字符串插入, 后面再解构成数组. 具体代码如下.
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permuteUnique = function(nums) {
    const n = nums.length
    let used = new Array(n).fill(0)
    let ansSet = new Set()
    
    const dfs = (nums, d, n, used, curr, ansSet) => {
        if (d === n) {
            // 化为字符串添加进去
            ansSet.add(curr.join(','))
            return
        }
        
        for (let i = 0; i < n; ++i) {
            if (used[i]) continue
            used[i] = 1
            curr.push(nums[i])
            dfs(nums, d + 1, n, used, curr, ansSet)
            used[i] = 0
            curr.pop()
        }
    }
    
    dfs(nums, 0, n, used, [], ansSet)
    
    // 将 Set 转换为 Array
    let ans = Array.from(ansSet)
        .map(item => {
            let tmp = item.split(',')
            return tmp.map(Number)
        })
    return ans
};
  1. 剪枝操作. 什么意思呢? 就是我们在递归的过程中直接跳过会重复的排列. 怎么做到呢? 分为两步, 第一步, 将数组排序, 这样相同的元素在数组中就是相邻的; 第二步, 也是最重要的一步, 当遍历到相同元素时, 如果它不是第一位且它前面的相同的元素没有被使用, 那么就跳过这次递归. 为什么是这个条件呢? 比如 [1, 1, 2] 前两个组合为 [1, 1, 2], [1, 2, 1], 下一次递归时, curr 数组为 [], 递归到了第二个 1 上, 也就是下标为 1 的位置, 如果 curr 再以这个 1 为开始, 那么后面遍历到的肯定是重复的; 还有一个条件是前面的元素没有被使用, 这是因为当前面的 1 未使用时, 说明下一个 1 一定是可以跳过的, 前面的 1 已经包括了以 1 开头的所有组合. 代码见下方.
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permuteUnique = function(nums) {
    const n = nums.length
    let path = []
    let res = []
    nums = nums.sort((a,b) => a - b)
    let use = new Array(n).fill(0)
    
    const dfs = (d) => {
      if (d === n) {
        res.push([...path])
        return
      }
      for (let i = 0; i < n; i++) {
        if (use[i]) continue
        if (i > 0 && nums[i] === nums[i-1] && !use[i-1]) continue
        path.push(nums[i])
        use[i] = 1
        dfs(d+1)
        use[i] = 0
        path.pop()
      }
    }
    
    dfs(0)
    return res
};