1. 题目描述
46
Given a collection of distinct numbers, return all possible permutations.For example,
[1,2,3] have the following permutations:
[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].47
Given a collection of numbers that might contain duplicates, return all possible unique permutations.For example,
[1,1,2] have the following unique permutations:
[1,1,2], [1,2,1], and [2,1,1].
2. 解题思路
首先对于元素各异的情况,使用基于交换的回溯方法可以很容易解决
然后是有重复元素的情况,对于这种情况, 我们首先想到的是按照元素各异的情况处理, 然后通过排序删除重复部分, 然而, 当我们实践的时候, 得到了TLE。
这样, 我们就不得不仔细思考, 应该加入的元素的实际情况。
1. 如果两个相互需要交换的元素是相同的值的话, 就可以直接pass了, 否则, 这个序列会被加入两次
2. 从起始元素到当前元素, 如果元素的值发生重复的话, 我们也应该pass
- 这点理解起来有些不是那么直观(举个栗子)
3. code
3.1 46
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
if (nums.size() == 0)
return res;
helper(nums, res, 0);
return res;
}
private:
void helper(vector<int>& nums, vector<vector<int>> & res, int depth){
if (depth == nums.size()){
res.push_back(nums);
return;
}
for (int i = depth; i < nums.size(); i++){
swap(nums[i], nums[depth]);
helper(nums, res, depth + 1);
swap(nums[i], nums[depth]);
}
}
};
3.2 47
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> res;
if (nums.size() == 0)
return res;
sort(nums.begin(), nums.end());
helper(nums, res, 0);
//sort(res.begin(), res.end());
//res.erase(unique(res.begin(), res.end()), res.end());
return res;
}
private:
void helper(vector<int>& nums, vector<vector<int>> & res, int depth){
if (depth == nums.size()){
res.push_back(nums);
return;
}
for (int i = depth; i < nums.size(); i++){
if ((i != depth && nums[i] == nums[depth]))
continue;
// 找重复出现元素
bool flag = false;
for (int j = depth; j != i; j++){
if (nums[j] == nums[i]){
flag = true;
break;
}
}
if (flag)
continue;
swap(nums[i], nums[depth]);
helper(nums, res, depth + 1);
swap(nums[i], nums[depth]);
}
}
};
4. 大神解法
4.1 47 demo1
这个解法, 说实话, 到现在还是理解起来比较困难, 尤其是recursion 操作 之后, 没有swap 操作, 感觉很神奇~~
class Solution {
public:
void recursion(vector<int> num, int i, int j, vector<vector<int> > &res) {
if (i == j-1) {
res.push_back(num);
return;
}
for (int k = i; k < j; k++) {
if (i != k && num[i] == num[k]) continue;
swap(num[i], num[k]);
recursion(num, i+1, j, res);
}
}
vector<vector<int> > permuteUnique(vector<int> &num) {
sort(num.begin(), num.end());
vector<vector<int> >res;
recursion(num, 0, num.size(), res);
return res;
}
};
4.2 next_permutations
这种解法, 巧妙的将permuation 转化为 next_permuation 进行处理, brilliant !!!
/*
yes,you are right.Actually,it is a problem similar to "Next Permutation", which rearranges numbers into the lexicographically next greater permutation of numbers. the difference between these two problems lies in how permutations we are required to generate.sorry,my English level is limited,hope this might help.
*/
bool nextPermutation(vector<int> &num) {
int len=num.size()-1;
for(int pos=len-1;pos>=0;--pos){
if(num[pos]<num[pos+1]){
int smallNum=num[pos];
int lastPosBiggerThanSmallNum=
len-(find_if(num.rbegin(),
num.rend(),(bind2nd(greater<int>(),smallNum)))-
num.rbegin());
swap(num[pos],num[lastPosBiggerThanSmallNum]);
sort(num.begin()+pos+1,num.end());
return true;
}
}
return false;
}
vector<vector<int> > permuteUnique(vector<int> &num){
vector<vector<int> > res;
if(num.empty())
return res;
sort(num.begin(),num.end());
res.push_back(num);
while(nextPermutation(num)){
res.push_back(num);
}
return res;
}
4.3 DFS + map
借助 map, 巧妙的化解重复元素的危机
I see most solutions are using next permutation. That's great and only uses O(1) space.
Anyway I am sharing backtracking solution which uses O(n) space. This is actually a typical backtracking problem. We can use hash map to check whether the element was already taken. However, we could get TLE if we check vector num every time. So we iterate the hash map instead.
class Solution {
public:
vector<vector<int> > permuteUnique(vector<int> &num) {
vector<vector<int>> v;
vector<int> r;
map<int, int> map;
for (int i : num)
{
if (map.find(i) == map.end()) map[i] = 0;
map[i]++;
}
permuteUnique(v, r, map, num.size());
return v;
}
void permuteUnique(vector<vector<int>> &v, vector<int> &r, map<int, int> &map, int n)
{
if (n <= 0)
{
v.push_back(r);
return;
}
for (auto &p : map)
{
if (p.second <= 0) continue;
p.second--;
r.push_back(p.first);
permuteUnique(v, r, map, n - 1);
r.pop_back();
p.second++;
}
}
};
4.4 DFS + flag
使用一个 bool 标记, 标记当前元素值是否已经被处理过, 如果处理过了, 下次遇到相同的值之后, 可以直接pass
/*
Use an extra boolean array " boolean[] used" to indicate whether the value is added to list.
Sort the array "int[] nums" to make sure we can skip the same value.
when a number has the same value with its previous, we can use this number only if his previous is used
*/
public class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
if(nums==null || nums.length==0) return res;
boolean[] used = new boolean[nums.length];
List<Integer> list = new ArrayList<Integer>();
Arrays.sort(nums);
dfs(nums, used, list, res);
return res;
}
public void dfs(int[] nums, boolean[] used, List<Integer> list, List<List<Integer>> res){
if(list.size()==nums.length){
res.add(new ArrayList<Integer>(list));
return;
}
for(int i=0;i<nums.length;i++){
if(used[i]) continue;
if(i>0 &&nums[i-1]==nums[i] && !used[i-1]) continue;
used[i]=true;
list.add(nums[i]);
dfs(nums,used,list,res);
used[i]=false;
list.remove(list.size()-1);
}
}
}