难度: 中等
题目描述
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
思路1
记录每个数字还能够被选择的次数numCnt
,在选择数字时用numCnt
代替used
。
代码
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<int> now_p;
vector<vector<int>> all_p;
vector<pair<int, int>> numCnt;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size();) {
int num = nums[i], cnt = 1;
while (i + cnt < nums.size() && nums[i + cnt] == num) ++cnt;
i += cnt;
numCnt.emplace_back(num, cnt);
}
function<void(int index)> generate;
generate = [&generate, &nums, &numCnt, &now_p, &all_p](int index) {
if (index == nums.size()) {
all_p.push_back(now_p);
return;
}
for (auto &i : numCnt) {
if (i.second) {
--i.second;
now_p.emplace_back(i.first);
generate(index + 1);
now_p.pop_back();
++i.second;
}
}
};
generate(0);
return all_p;
}
};
思路2
C++标准库中有一个函数next_permutatuin
,46和47题都可以用这个水过,这个函数只需要给出一个排列,就可以返回按照字典序的下一个排列,而且即使有重复元素也没问题,它是怎么实现的呢?cppreference 上给出了一种可能的实现:
template<class BidirIt>
bool next_permutation(BidirIt first, BidirIt last)
{
if (first == last) return false;
BidirIt i = last;
if (first == --i) return false;
while (true) {
BidirIt i1, i2;
i1 = i;
if (*--i < *i1) {
i2 = last;
while (!(*i < *--i2))
;
std::iter_swap(i, i2);
std::reverse(i1, last);
return true;
}
if (i == first) {
std::reverse(first, last);
return false;
}
}
}
按照字典序,最小的排列是不递减的排列,最大的排列是不递增的排列。而且对于任意一种排列,它的右侧都有一段不递增的序列(长度可以是1)假设这段排列右侧不递增的部分的长度是n,那么按照字典序,紧挨着这个排列的下一个排列应该是:
- 从右侧开始数,第n+1个位置之后的元素不变。
- 第n+1个位置的元素应该变大,但是又要尽可能得小(才能满足紧邻),这就要从右侧的n个元素中选择一个恰好大于a[n+1]的元素,二者互换。
- 右侧n个位置按照不递减排序(reverse一下就行)。
看懂了这段代码之后我感觉我对这个问题的理解都加深了,把这段代码的思想用到这道题里就有了下面的代码。
代码
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> all_p;
function<void(int index)> generate;
generate = [&generate, &nums, &all_p](int index) {
if (index == nums.size() - 1) {
all_p.push_back(nums);
return;
}
while (true) {
generate(index + 1);
if (nums[index] >= nums[index + 1]) {
// if (index == 0) reverse(nums.begin(), nums.end()); // 把num恢复
break;
}
// 使用lower_bound
// auto out = lower_bound(nums.begin() + index + 1, nums.end(), nums[index], greater<>()) - 1;
// swap(nums[index], *out);
int out = nums.size();
while (nums[--out] <= nums[index]);
swap(nums[index], nums[out]);
reverse(nums.begin() + index + 1, nums.end());
}
};
generate(0);
return all_p;
}
};