47. Permutations II

原题链接

47. Permutations 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. Permutations

先简单回顾一下之前求全排列的方法,即 46. Permutations 的解法,就是一道标准的递归题,代码如下所示:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> res;
        helper(nums, 0, res);
        return res;
    }

    void helper(vector<int>& nums, int start, vector<vector<int>>& res){
        if(start == nums.size()){
            res.push_back(nums);
            return;
        }

        for(int i = start; i < nums.size(); i++){
            swap(nums[start], nums[i]);
            helper(nums, start+1, res);
            swap(nums[start], nums[i]);
        }
    }

    void swap(int& a, int& b){
        int tmp = a;
        a = b; 
        b = tmp;
    }
};

具体分析如下,假如我们现在需要求的是 {1,2,3} 的全排列,那么过程如下:

  • 先把1固定在第一位,然后递归求后面 {2,3} 的全排列
  • 把1和后面的每一位交换,然后求后面剩下的全排列,递归完成记得交换回来,保证下次交换之前恢复原样,该过程如下:
    • 交换1和2,得到 {2, 1, 3},递归求解 {1,3}的全排列,递归完成后交换回来,恢复原样
    • 交换1和3,得到 {3, 2, 1},递归求解 {2,1}的全排列,递归完成后交换回来,恢复原样
    • 递归的过程跟本次过程一样

现在假设序列中出现了一个重复数字,例如序列 {1, 2, 3, 2},为了区分其中的两个2,我们给它们分别取一个下标,于是序列可以表示成 {1, 2a, 3, 2b}

依然按照上面的方法,我们发现,存在以下2个情况:

  • 1和2a交换,得到 {2a, 1, 3, 2b},然后递归求 {1, 3, 2b} 的全排列
  • 1和2b交换,得到 {2b, 2a, 3, 1},然后递归求 {2a, 3, 1} 的全排列

但是我们会发现,{1, 3, 2b}的全排列{2a, 3, 1}的全排列 是会重复的,问题出在哪呢?

仔细思考会发现,1和后面两个2交换效果其实是一样的,这势必会导致两次交换后会出现相同的全排列,为了解决这个问题,我们需要在一轮交换过程中排除那些重复的交换。

所以我们借助一个set(集合)来解决,于是,在存在相同数字的序列中求全排列的方法为:

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> res;
        helper(nums, 0, res);
        return res;
    }

    void helper(vector<int>& nums, int start, vector<vector<int>>& res){
        if(start == nums.size()){
            res.push_back(nums);
            return;
        }

        unordered_set<int> history;
        for(int i = start; i < nums.size(); i++){
            if(history.count(nums[i])) continue;
            history.insert(nums[i]);
            
            swap(nums[start], nums[i]);
            helper(nums, start+1, res);
            swap(nums[start], nums[i]);
        }
    }

    void swap(int& a, int& b){
        int tmp = a;
        a = b; 
        b = tmp;
    }
};

和之前的代码对比,发现只有下面几行代码不一样

unordered_set<int> history;
for(int i = start; i < nums.size(); i++){
    if(history.count(nums[i])) continue;
    history.insert(nums[i]);
posted @ 2021-07-06 19:34  nullxjx  阅读(30)  评论(0编辑  收藏  举报