给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
如果不考虑去重,那么这道题就是一道非常基础的递归题目,比较基础。这里主要说一下去重的过程,去重的思想倒是不难想到,每一位每一种数字只能出现一次。
比如数组[1,1,2],递归时让第一位是1,然后第二位是1,第三位是2,一次递归完成没有问题,得到排列[1,1,2],于是保存答案进行回溯,回溯到第二位,让第二位为2,注意第二位是第一次出现了2,所以没问题,再让第三位为1,于是又有了一个正确的排列[1,2,1],保存回溯。
接着回溯到了第二位,第二位已经没有可以放的数字了,于是又回溯到第一位,这时候第一位应该选择第二个数,也就是1,可是第一位已经出现过数字1了,这种情况就不能选择,也就是说只要遇到这种情况能正确退出我们就可以去重了。
根据这种思路就能得到第一种去重,定义一个occur[i][j]数组,代表第i位是否出现过j这个数字。
add是整个给定数组中的最小值,然后取反,再加回到原数组就能保证整个数组都是正数了,如果不这样数组中有负数occur数组会越界。
这样写的关键是考虑occur数组清空的时机,我一开始认为occur数组根本不用清空(即不写下面代码中的memset),但是我发现[1,1,2]这个样例我就过不去,不清空就不能输出[2,1,1]这个答案,这是因为第二位已经出现过1这个数字了,当第一位选择2这个数字时,不清空就代表我第二位不能选择1这个数字,这显然不合理。
因为第二位刚才出现1时,第一位也是1,现在我第一位已经变成数字2了,我第二位当然可以选择数字1了。所以每次当回溯的时候,我们要把occur[now]清零(now代表现在是第now位在选择数字),因为前面now-1位的数字不同,now位可选择的数字当然也不同。
这是我的想法,想法比较好想到,但是占用的空间较大。
class Solution {
public:
int occur[105][1005];
int vis[105];
int num[105];
vector<vector<int>>ans;
int add;
void dfs(int now,int sum,int size,vector<int> nums)
{
if(sum==size)
{
vector<int>tmp;
for(int i=0;i<size;i++)
tmp.push_back(num[i]);
ans.push_back(tmp);
return;
}
for(int i=0;i<size;i++)
{
if(vis[i]==1||occur[now][nums[i]+add])
continue;
num[now]=nums[i];
vis[i]=1;
occur[now][nums[i]+add]=1;
dfs(now+1,sum+1,size,nums);
vis[i]=0;
}
memset(occur[now],0,sizeof(occur[now]));//**注意清空occur的时机,这一位全部遍历完要回溯时就清空。**
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
int size=nums.size();
for(int i=0;i<size;i++)
add=min(add,nums[i]);
add=-add;
dfs(0,0,size,nums);
return ans;
}
};
下面说说答案的做法,答案是这么想的,对于一串连续相同的数字,我们选择数字的顺序只能有一种,比如[1,1,1,2]这组数,假设现在从第0位到第2位要选择这三个连续相同的1,我让第0位到第2位分别选择第1,2,3个1即可,当然你可以让第0位到第2位分别选择第3,2,1个1,甚至你可以让0到2位选择第3,1,2个1,总之不论按照什么顺序,你只需要确定这种顺序只有一种,那么就不会产生重复。
假如我选择了两种顺序,我让0到2位既选择1,2,3个1,又选择3,2,1个1,那么必然会出现重复。
官方题解给了这种去重的代码,如果我们按照1,2,3的顺序选择1,那么代码就是这样的:
if(i>0&&nums[i]==nums[i-1]&&!vis[i-1])
continue;
如果前一位还被没有访问过,那么肯定不是1,2,3的顺序,所以continue。
当然也可以按照3,2,1的顺序选择,这样的代码就变成了:
if(i>0&&nums[i]==nums[i-1]&&vis[i-1])
continue;
如果前一位被选择过,这就说明肯定不是3,2,1的顺序,直接跳出即可。
完整代码如下:
class Solution {
public:
int vis[105];
int num[105];
vector<vector<int>>ans;
void dfs(int now,int sum,int size,vector<int> nums)
{
cout<<now<<" "<<sum<<endl;
if(sum==size)
{
vector<int>tmp;
for(int i=0;i<size;i++)
tmp.push_back(num[i]);
ans.push_back(tmp);
return;
}
for(int i=0;i<size;i++)
{
if(vis[i]==1||(i>0&&nums[i]==nums[i-1]&&vis[i-1]))
continue;
cout<<i<<endl;
num[now]=nums[i];
vis[i]=1;
dfs(now+1,sum+1,size,nums);
vis[i]=0;
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
int size=nums.size();
sort(nums.begin(),nums.end());
dfs(0,0,size,nums);
return ans;
}
};