题意分析
给你一个序列,需要先将这个序列的相同的元素去掉,也就是先去重。然后按照原来的序列进行一个全排列,需要对每个排列进行字典序排列。
前置知识
- 我们按照顺序来。
- 首先,你需要知道怎么去重,在C++里面,对数组的去重两种方法,一种是自己实现,先对整个数组进行排序,然后两两相邻的进行比较。一旦出现不同就放入集合里面。另一种是使用C++的库函数unique。其实差别不大。
- 然后,我们说一下什么是全排列。全排列就是给你若干个数字,将这些数字混乱排列得到的所有的结果。
- 最后,我们来说一下什么是字典序。就是按照ASCII表里面出现的顺序,从小到大输出。比如,两个字符串abc,abb,显然,对于前两个字符,都是一样的,比较不出来,但是第三个字符明显后者比前者更小。所以后者的字典序更小。这也是大多数字符串排序算法实现的基本原理。
思路分析
解法一 C++库函数
首先,我们来说一个简单的,就是称为调库工程师。我们直接利用C++为我们实现好的库函数进行全排列。也就是,我们使用next_permutation函数。具体这个函数的实现可以自己先百度进行了解。然后,我们先利用unqiue去重。之后就可以直接用啦!
代码如下
- 根据数学上面的知识,全排列的时间复杂度为O(n!)
- 除了结果数组和参数,只使用了少数变量,空间复杂度为O(1)
class Solution { public: vector<vector<int> > permute(vector<int> &num) { // 特判为空的情况 vector<vector<int> > ans; if(!num.size()){ return ans; } sort(num.begin(),num.end()); // 利用C++的库函数进行处理 do{ ans.push_back(num); }while(next_permutation(num.begin(),num.end())); return ans; } };
解法二 DFS回溯算法
- 显然,如果想真正学习算法的人肯定不会止步于上一次,为什么我们不自己实现呢?对了,学习阶段还是需要自己多动手实现。于是,我们可以自己利用DFS回溯算法求全排列。我们先利用二叉树的DFS回溯来帮助大家了解什么是回溯算法吧,其实题的实现远远比二叉树的要简单。
而在本题目中,我们只需要在遍历的时候标记那些访问过的点,对于没有访问过的点,我们继续向下递归,然后标记上,在递归结束返回之后,我们需要做的是把标记清楚。依次进行处理。这样,我们每个结点的位置在任何的位置都有可能,也就是实现了全排列的情况。
代码如下
- 根据数学上面的知识,全排列的时间复杂度为O(n!)
- 使用了一个unordered_map类型的vis进行标记,空间复杂度为O(n)
class Solution { public: vector<vector<int> > ans; unordered_map<int,bool> vis; void dfs(int cnt,int n,vector<int>& num,vector<int>& tmp){ // 递归边界 if(cnt==n){ ans.push_back(tmp); return; } for(auto x:num){ if(!vis[x]){ // 如果这个元素没有被访问,那么进行访问 vis[x]=true; tmp.push_back(x); dfs(cnt+1,n,num,tmp); // 访问完之后进行回溯 tmp.pop_back(); vis[x]=false; } } } vector<vector<int> > permute(vector<int> &num) { // 特判为空的情况 if(!num.size()){ return ans; } sort(num.begin(),num.end()); // 进行DFS回溯求解 vector<int> tmp; dfs(0,num.size(),num,tmp); return ans; } };