题意分析

  • 首先,题目给出一个大小为n的数组,然后,需要我们求出这个数组的全排列。但是要注意的是这个数组里面可能存在相同的数字。

思路分析

解法一 库函数实现

  • 学习过C++的同学应该都直到,C++有一个库函数next_permutation可以用来处理数组的全排列问题,但是这个库函数需要我们先对这个数组进行升序排序。具体我们来看一下源码。
template<class _BidIt> inline
bool _Next_permutation(_BidIt _First, _BidIt _Last)
{ // permute and test for pure ascending, using operator<

//-----------------------------------------------\
_DEBUG_RANGE(_First, _Last);
_BidIt _Next = _Last;
if (_First == _Last || _First == --_Next)
   return (false);

//检查边界

//-----------------------------------------------/

// 我们看到,这里写了一个空循环
for (; ; )
   { // find rightmost element smaller than successor
   _BidIt _Next1 = _Next;
   if (_DEBUG_LT(*--_Next, *_Next1))
    { // swap with rightmost element that's smaller, flip suffix
    _BidIt _Mid = _Last;
    for (; !_DEBUG_LT(*_Next, *--_Mid); )
     ;
    std::iter_swap(_Next, _Mid);

//从右往左找到相邻的两个,右边比左边大,左边那个就是需要被替换的,然后从右边找到一个比这个元素大的,交换这两个元素的位置
    std::reverse(_Next1, _Last);

// 交换之后,翻转交换元素的后面的所有元素
    return (true);
    }

   if (_Next == _First)
    { // pure descending, flip all
    std::reverse(_First, _Last);
    return (false);
    }
   }
}
  • 具体请看代码
    • 需要进行全排列,根据数学中排列组合的知识,全排列的时间复杂度为O(n!)
    • 除了参数和结果数组基本没有使用其余变量,空间复杂度为O(1)
class Solution {
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
        vector<vector<int> > ans;
        sort(num.begin(),num.end()); // 注意,全排列需要先对数组进行升序排列
        do{
            ans.push_back(num);
            // 利用C++的库函数进行遍历所有的排列
        }while(next_permutation(num.begin(), num.end()));
        return ans;
    }
};
  • 最后说一句,如果是为了学习算法,还是不要偷懒,最好还是自己可以实现这个功能,不然注定只是一名调库工程师。所以我们下面来为大家介绍如何利用DFS的回溯解决这个问题。

解法二 回溯法

  • 首先,我默认大家都已经了解了深度优先搜索的基础知识。如果有不了解的,请看这个传送门.
  • 然后,我给大家回一个图帮助大家更好地理解什么是回溯?
    图片说明

其实就类似于我们日常的撤回功能,我们在进行某一种行为的时候这个状态改变了,但是我们发现这个行为是错误的,我们就需要进行撤回操作,恢复执行这个行为之前的状态。当然,真正的回溯其实远不是如此,这个只是帮助大家理解。

  • 好了,我们接下来说一下DFS操作,递归参数为遍历到的数字,递归出口为数字到了num数组的大小,然后就是遍历数组,我们需要对没有被遍历到的数字执行递归操作,递归前需要将这个位置标记为访问了,同时存入,递归出来后需要将这个位置重新标记为未访问,然后之前存入的数字弹出。最后记得给得到的序列去重即可。

  • 代码如下

    • 全排列的时间复杂度未O(n!)
    • 使用了unordered_map和一些中间的vector进行处理,空间复杂度为O(n)
class Solution {
public:
    unordered_map<int,bool> mp;
    vector<vector<int> > ans;
    vector<int> tmp,now;
    int len;
    void dfs(int cnt){  // cnt表示当前遍历到的数字的个数
        if(cnt==len+1){ // 递归出口
            ans.push_back(now);
            return;
        }

        for(int i=0;i<len;i++){
            if(mp[i]==0){
                // 如果这个数字没有被访问,先将这个数字放到数组里面
                now.push_back(tmp[i]); 
                mp[i]=1; // 然后进行标记
                dfs(cnt+1);
                mp[i]=0;  // 进行回溯
                now.pop_back();
            }
        }
    }
    vector<vector<int> > permuteUnique(vector<int> &num) {
        tmp=num;
        len=num.size();
        dfs(1);
        // 可能存在重复的数字,所以需要进行去重处理
        vector<vector<int> > res;
        sort(ans.begin(),ans.end());
        int m=unique(ans.begin(),ans.end())-ans.begin();
        for(int i=0;i<m;i++){
            res.push_back(ans[i]);
        }
        return res;
    }
};