原题链接
题目描述
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
问题分析
这是一道求全排列的问题,之前有一道和这道题类似的题,那道题中不存在重复的数字,见 46. Permutations
先简单回顾一下之前求全排列的方法,即 46. Permutations 的解法,就是一道标准的递归题,代码如下所示:
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
helper(nums, 0, res);
return res;
}
void helper(vector<int>& nums, int start, vector<vector<int>>& res){
if(start == nums.size()){
res.push_back(nums);
return;
}
for(int i = start; i < nums.size(); i++){
swap(nums[start], nums[i]);
helper(nums, start+1, res);
swap(nums[start], nums[i]);
}
}
void swap(int& a, int& b){
int tmp = a;
a = b;
b = tmp;
}
};
具体分析如下,假如我们现在需要求的是 {1,2,3} 的全排列,那么过程如下:
- 先把1固定在第一位,然后递归求后面 {2,3} 的全排列
- 把1和后面的每一位交换,然后求后面剩下的全排列,递归完成记得交换回来,保证下次交换之前恢复原样,该过程如下:
- 交换1和2,得到 {2, 1, 3},递归求解 {1,3}的全排列,递归完成后交换回来,恢复原样{1, 2, 3}
- 交换1和3,得到 {3, 2, 1},递归求解 {2,1}的全排列,递归完成后交换回来,恢复原样{1, 2, 3}
- 递归的过程跟本次过程一样
现在假设序列中出现了一个重复数字,例如序列 {1, 2, 3, 2},为了区分其中的两个2,我们给它们分别取一个下标,于是序列可以表示成 {1, 2a, 3, 2b}
依然按照上面的方法,我们发现,存在以下2个情况:
- 1和2a交换,得到 {2a, 1, 3, 2b},然后递归求 {1, 3, 2b} 的全排列
- 1和2b交换,得到 {2b, 2a, 3, 1},然后递归求 {2a, 3, 1} 的全排列
但是我们会发现,{1, 3, 2b}的全排列 和 {2a, 3, 1}的全排列 是会重复的,问题出在哪呢?
仔细思考会发现,1和后面两个2交换效果其实是一样的,这势必会导致两次交换后会出现相同的全排列,为了解决这个问题,我们需要在一轮交换过程中排除那些重复的交换。
所以我们借助一个set(集合)来解决,于是,在存在相同数字的序列中求全排列的方法为:
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> res;
helper(nums, 0, res);
return res;
}
void helper(vector<int>& nums, int start, vector<vector<int>>& res){
if(start == nums.size()){
res.push_back(nums);
return;
}
unordered_set<int> history;
for(int i = start; i < nums.size(); i++){
if(history.count(nums[i])) continue;
history.insert(nums[i]);
swap(nums[start], nums[i]);
helper(nums, start+1, res);
swap(nums[start], nums[i]);
}
}
void swap(int& a, int& b){
int tmp = a;
a = b;
b = tmp;
}
};
和之前的代码对比,发现只有下面几行代码不一样
unordered_set<int> history;
for(int i = start; i < nums.size(); i++){
if(history.count(nums[i])) continue;
history.insert(nums[i]);