【LeetCode】每日一题 全排列 II

63 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情

47. 全排列 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]]
「提示:」
1 <= nums.length <= 8
-10 <= nums[i] <= 10

解题思路

// 第一种
用回溯解决,与46. 全排列类似,但是nums可以有重复元素。
​
那么就要在同一层去重,去重条件为当当前元素与前一个元素相同,且前一个元素未在used里标记为true。即前一个元素在同一层被使用过了,并未在上一层被使用。
​
然后要进行递归,若该元素未被标记为true,则可以进入递归。否则跳过此次循环。
​
作者:peterz-li
链接:https://leetcode.cn/problems/permutations-ii/solution/47-quan-pai-lie-ii-by-peterz-li-75b3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
​
// 第二种
全排列就不说了,要去重的功能,其实就是循环的时候,加一个对象缓存一下,下次再遇到这个数,开头的结果就直接略过了
​
// 第三种
重点在如何去重和剪枝。现在有两个要去重:
​
选取的元素下标不能重复,保证每个元素都被选到。我这里选择使用 item {set} 来保持下标
每层选取的元素的值不能重复。参考 集合II,使用预排序 + uesd{set} 的方法保证不会出现这种情况:
nums = [2,2],result = [[nums[0], nums[1]], [nums[1], nums[0]]]
​

代码实现

// 第一种
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permuteUnique = function (nums) {
    nums.sort((a, b) => a - b);
    const res = [], path = [];
​
    const backtracking = function (used) {
        if (path.length === nums.length) {
            res.push([...path]);
            return;
        }
        for (let i = 0; i < nums.length; i++) {
            if (i > 0 && nums[i] === nums[i - 1] && !used[i - 1]) {
                continue;
            }
            //当数组未被定义时,undefined === false 的结果为false
            if (!used[i]) {
                used[i] = true;
                path.push(nums[i]);
                backtracking(used);
                path.pop();
                used[i] = false;
            }
        }
    }
​
    backtracking([]);
    return res;
};
​
// 第二种
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
      var permuteUnique = function (nums) {
        if (nums.length === 1) {
          return [nums];
        }
        let res = [], map = {};
        nums.forEach((e, index) => {
          if (!map[e]) {
            map[e] = 1;
            let arr = [...nums];
            arr.splice(index, 1);
            permuteUnique(arr).forEach((e1) => {
              let arr = [e, ...e1];
              res.push(arr);
            });
          }
        });
        return res;
      };
​
// 第三种
// 相比于全排列问题,这道题从 nums 元素不重复变成了可重复。那我们沿用 集合II 问题的思路,预排序 + used 来处理即可解决每层 for 循环重复选取相同大小元素的问题。
// 其实就是 集合II 问题少了 startIndex 的剪枝 + 全排列保证元素下标不重复。
​
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permuteUnique = function(nums) {
    const result = [], item = new Set() // item 存下标
​
    function backtracking() {
        if (item.size >= nums.length) {
            result.push([...item].map(numsIndex => nums[numsIndex]))
            return
        }
        
        let used = new Set()
​
        for (let i = 0; i < nums.length; i++) {
            // 保证每层选取的元素大小不重复
            if (used.has(nums[i])) {
                continue
            }
​
            // 保证下标不重复
            if (item.has(i)) {
                continue
            }
​
            used.add(nums[i])
            item.add(i)
            backtracking()
            item.delete(i)
        }
    }
​
    nums.sort()
​
    backtracking()
    
    return result
};

如果你对这道题目还有疑问的话,可以在评论区进行留言;