您的位置:首页 > 其它

Leetcode 47. Permutations II

2017-01-28 09:13 127 查看
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],

[2,1,1]

]

s思路:

1. 方法1,用backtracking,和之前的一道题一样,除了需要先排序,方便通过比较前后两个数是否相等。需要怎么检测重复呢

2. 为找到判断条件,需要理解backtracking的精神实质:backtracking = for+recursive。recursive是处理不同层次的数,即:纵向的深度的方向的遍历,即:不同的位置坐标;for是用在同一个层次的数,即:横向的遍历,即:同一个位置上尝试不同的数。

例如:[1,1,1,2,2,3], recursive是位置坐标从0-5的遍历,每一个位置代表一个层次,在每一个层次(位置)上,有多种可能的选择。

3. 为找到重复的组合,如果在同一个层次(位置)上,已经选用过某一个数,那么在同一个位置上再碰到这个数,就需要skip,因为在第一次遇到这个数的时候已经尝试了所有可能的组合。所以,

i>0&&nums[i-1]==nums[i]&&!used[i-1]


即:nums[i-1]==nums[i]表示数据一样,!used[i-1]表示i-1这个数是这个层次遍历后复位为0的位置,也就是说,i-1一定是这个层次先遍历的一种可能,同一个层次尝试nums[i-1]之后,需要复位对应的标志位,才进入同一个层次下一个位置nums[i];如果used[i-1]==1,则说明i-1这个位置是之前某个层次(位置)正在遍历并置位为1,和当前不是一个层次,这种情况下,就不能skip。

//方法1:到底叫backtracking,还是DFS?
class Solution {
public:
void helper(vector<vector<int>>&res,vector<int>& nums,vector<int> cur,vector<bool>&used,int pos){
if(pos==nums.size()){
res.push_back(cur);
return;
}
for(int i=0;i<nums.size();i++){
if(used[i]||i>0&&nums[i-1]==nums[i]&&!used[i-1]) continue;//需要解释下!!
cur.push_back(nums[i]);
used[i]=1;
helper(res,nums,cur,used,pos+1);
used[i]=0;
cur.pop_back();
}
}
vector<vector<int> > permuteUnique(vector<int> &num) {
//
sort(nums.begin(),nums.end());
vector<vector<int>> res;
vector<bool> used(nums.size(),0);
helper(res,nums,{},used,0);
return res;
}
};


方法2:

1. 用交换的方法,为了判断重复,序列要先排序。但是,交换元素又会破坏数据的sorting的性质。因此,交换和sorting对同一个序列操作,必然导致不好处理的情况,或者说交换是适合没有重复元素的序列做permutation的,其对含有重复元素序列并不理想。

2. 不过网上还是有一种比较简洁的方法用交换+sorting:举例:

P[1,1,1,2,2,2,3,3]=
{[1]+P[1,1,2,2,2,3,3]}+
{[2]+P[1,1,1,2,2,3,3]}+
{[3]+P[1,1,1,2,2,2,3]}


注意看:和普通的交换不同,每次交换后,不用交换回来,这样就可以保证sorting+交换的compatibility.上面例子就是: 1和2交换后,2就放在第一个位置了,同一个层次的遍历后面数据是,直接用2和3交换。

//方法2:交换位置+backtracking:代码短,省空间,速度快!
class Solution {
public:
void helper(vector<vector<int>>&res,vector<int> nums,int start){
if(start==nums.size()){
res.push_back(nums);
return;
}
for(int i=start;i<nums.size();i++){
if(i!=start&&nums[start]==nums[i]) continue;
swap(nums[start],nums[i]);
helper(res,nums,start+1);
}
}
vector<vector<int> > permuteUnique(vector<int> &nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> res;
helper(res,nums,0);
return res;
}
};


方法3:

1. 用map来做,把数存在vector,相同的数还是分散的,可以用map把相同的数放在一起,这样就不担心重复了!而且实际中,都没有必要使用map,用unordered_map就足够了!

2. 这个例子也说明了,数据结构对解题的思路的帮助!用map把相同数放在一起,现在map就相当没有重复元素的vector了。以后处理重复元素,就多了一个思路:把相同元素group起来!

//方法3:map
class Solution {
public:
vector<vector<int> > permuteUnique(vector<int> &num) {
unordered_map<int,int> mm;
for (int i : num)
{
mm[i]++;
}
vector<vector<int>> res;
vector<int> cur;
helper(res,mm,cur,num.size());
return res;
}

void helper(vector<vector<int>>&res,unordered_map<int,int>&mm,vector<int>&cur,int count){
if(count==0){
res.push_back(cur);
return;
}
for(auto&p:mm){//超级大bug:写成auto p:mm就不能修改mm的值,
//要修改mm的值,就一定要用auto&p:mm
if(p.second==0) continue;
p.second--;
cur.push_back(p.first);
helper(res,mm,cur,count-1);
cur.pop_back();
p.second++;
}
//不用上面的简洁形式,就只有下面笨重的iterator了。
/*for(auto p=mm.begin();p!=mm.end();p++){
if(p->second==0) continue;
p->second--;
cur.push_back(p->first);

helper(res,mm,cur,count-1);
cur.pop_back();
p->second++;
}*/
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode backtrack map dfs