047 Permutations II[M]

1 题目描述

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

难度:Medium

2 题目样例

For example,
[1,1,2] have the following unique permutations:

[
[1,1,2],
[1,2,1],
[2,1,1]
]

3 题意分析

给你一个数组,让你返回数组中所有元素的全排列。

和上一题的区别是,上一题中数组中的元素是互异的,而这道题目则不是。

4 思路分析

1.偷懒调用STL法:(逃

由于next_permutation函数生成的是”按照字典序的下一个排列”,所以根本不用担心有可能会生成重复的排列。

代码实现如下:

class Solution 

{
    
public:
       
   vector<vector<int>> permute(vector<int>& nums)
   
   {  
       
        vector<vector<int>> result;  

        sort(nums.begin(), nums.end());  

        do

        {  
            result.push_back(nums);  
        }

           while(next_permutation(nums.begin(), nums.end()));  

        return result;  
    }  
};

2.偷懒手写next_permutation函数法:

这次的代码和上一题的代码是一样的…完全可以直接拿来用。为什么可以拿来用呢?因为上一题我的做法实际上就是手动实现了一下next_permutation,跟STL同理,只会生成”按照字典序的下一个排列”,重复的排列是不会生成的。

代码实现如下:

class Solution 

{
    
public:
       
    vector<vector<int> > permute(vector<int> &num) 
    
    {
        vector<vector<int> > res;
        
        if(num.size() == 0)return res;
        
        sort(num.begin(), num.end());
        
        res.push_back(num);
        
        while(mynext_permutation(num))res.push_back(num);
        
        return res;
    }
     
    bool mynext_permutation(vector<int>&num)
        
    {
        int n = num.size();
        
       if(n <= 1)return false;
        
       for(int i = n-2, ii = n-1; i >= 0; i--,ii--)
           
       {
           
           if(num[i] < num[ii])
               
           {
               int j = n-1;
               
               while(num[j] <= num[i])j--;
               
               swap(num[i], num[j]);
               
               reverse(num.begin()+ii, num.end());
               
               return true;
           }
       }
        
       reverse(num.begin(), num.end());
        
       return false;
    }
};

3.出题人想考的做法:

很显然, Permutations题目被分为了两道,旨在考察数组中是否存在相同元素时生成排列方法的异同。既然如此我们就顺着出题人的思路走,重新审视这两道题目。

对于 Permutations I,我采用递归深搜的方法去看待它。对于包含n个元素的数组,先确定第一位置的元素,第一个位置有n中可能(每次把后面的元素和第一个元素交换),然后求子数组[2…n]的全排列。

代码实现如下:(注意这是Permutations I 的代码!)

class Solution {
public:
    vector<vector<int> > permute(vector<int> &num) {
        vector<vector<int> > res;
        if(num.size() == 0)return res;
        vector<int> tmpres;
        permuteRecur(num, 0, res, tmpres);
        return res;
    }
     
    void permuteRecur(vector<int> &num, int index, vector<vector<int> >&res, vector<int>&tmpres)
    {
        if(index == num.size())
        {
            res.push_back(tmpres);
            return;
        }
        for(int i = index; i < num.size(); i++)
            {
                swap(num[index], num[i]);
                tmpres.push_back(num[index]);
                permuteRecur(num, index+1, res, tmpres);
                tmpres.pop_back();
                swap(num[index], num[i]);
            }
    }
};

而对于包含有重复元素的数组,只需要在上述算法的基础上,注意以下几点即可:

当我们枚举第i个位置的元素时,若要把后面第j个元素和i交换,则先要保证[i…j-1]范围内没有和位置j相同的元素。具体实现可以在每次需要交换时进行顺序查找。

代码实现如下:

class Solution {
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
        vector<vector<int> > res;
        if(num.size() == 0)return res;
        vector<int> tmpres;
        permuteRecur(num, 0, res, tmpres);
        return res;
    }
     
    void permuteRecur(vector<int> &num, int index, vector<vector<int> >&res, vector<int>&tmpres)
    {
        if(index == num.size())
        {
            res.push_back(tmpres);
            return;
        }
        for(int i = index; i < num.size(); i++)
            if(i == index || !find(num, index, i, num[i]))
            {
                swap(num[index], num[i]);
                tmpres.push_back(num[index]);
                permuteRecur(num, index+1, res, tmpres);
                tmpres.pop_back();
                swap(num[index], num[i]);
            }
    }
    //从数组的[start,end)范围内寻找元素target
    bool find(vector<int> &num, int start, int end, int target)
    {
        for(int i = start; i < end; i++)
            if(num[i] == target)
                return true;
        return false;
    }
};

或者也可以采用哈希表去重的方式:

class Solution {
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
        vector<vector<int> > res;
        if(num.size() == 0)return res;
        vector<int> tmpres;
        permuteRecur(num, 0, res, tmpres);
        return res;
    }
     
    void permuteRecur(vector<int> &num, int index, vector<vector<int> >&res, vector<int>&tmpres)
    {
        if(index == num.size())
        {
            res.push_back(tmpres);
            return;
        }
        unordered_set<int> umap;
        for(int i = index; i < num.size(); i++)
            if(umap.find(num[i]) == umap.end())
            {
                umap.insert(num[i]);
                swap(num[index], num[i]);
                tmpres.push_back(num[index]);
                permuteRecur(num, index+1, res, tmpres);
                tmpres.pop_back();
                swap(num[index], num[i]);
            }
    }
};

5 后记

参考博客:LeetCode:Permutations, Permutations II(求全排列)

    原文作者:Lolita
    原文地址: https://zhuanlan.zhihu.com/p/34521538
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞