问题描述:
输入一组数字(可能包含重复数字),输出其所有的排列方式。
样例:
- 输入
[1,1,2] - 输出
[[1, 1, 2], [1, 2, 1], [2, 1, 1]]
采用递归的思路,对于一个给定的数组,首先要排好序,如果遇到数组里面有重复数字的情况,就要规定好这几个重复数字的出现顺序,比如上面的样例,第一个 1 出现之后,当放置第二个 1 的时候规定这个 1 在上一个 1 出现位置的后面,前提是第一个 1 放置的时候要从空缺位置中最前面的位置开始放置,否则这样的规定顺序会不成立。
首先确定start的起始位置,如果当前处理的是第一个位置的数字,或者,下一个数字与上一个数字不一致的时候,此时 start 可以设置为0.
其次判断当前位置是否空缺,没有被数字占用,使用到的是二进制。!(state>>i&1) 表示state移动 i 位置后,该位置是否为0,如果空缺则将nums[u]的值填入path[u]中,并在下次安排数字顺序的时候将该位置置为1。递归执行完毕后输出结果。
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> permutation(vector<int>& nums) {
path.resize(nums.size());
sort(nums.begin(),nums.end());
dfs(nums,0,0,0);
return ans;
}
void dfs(vector<int> &nums,int u,int start,int state)
{
if(u==nums.size())
{
ans.push_back(path);
return;
}
if(!u||nums[u]!=nums[u-1]) start=0;
for(int i=start;i<nums.size();i++)
{
if(!(state>>i&1))//判断某一位是否有空缺也就是否为0
{
path[i]=nums[u];
dfs(nums,u+1,i+1,state+(1<<i));//将用过的那位置为1
}
}
}
};
下面这个版本好理解,与leetcode 47 全排列Ⅱ题目类似。重复的部分来源于之前选择了产生了左子树,后来又选了2产生了右子树。因此需要根据排序后的数组相邻元素是否相等来去重。去重的原则是相邻的元素相等且后一个运算没有被访问过即语句i>=1 &&nums[i]==nums[i-1] &&!used[i-1] 其实去重逻辑中used[i-1]还可以是true,具体的解释看代码狂想录链接
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> permutation(vector<int>& nums) {
int n=nums.size();
vector<bool> used(n,false);
vector<int> path;
sort(nums.begin(),nums.end());
dfs(nums,used,path);
return res;
}
void dfs(vector<int>& nums,vector<bool>& used,vector<int>& path)
{
if(path.size()==nums.size())
{
res.push_back(path);
return;
}
for(int i=0;i<nums.size();i++)
{
if(!used[i])
{
if(i>=1 &&nums[i]==nums[i-1] &&!used[i-1]) continue;
used[i]=true;
path.push_back(nums[i]);
dfs(nums,used,path);
used[i]=false;
path.pop_back();
}
}
}
};