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]);
}
}
};