LeetCode 47
https://leetcode.cn/problems/permutations-ii/description/
难度:中等
高频面试题汇总:https://www.yuweihung.com/posts/2025/lc-hot/
本题是有重复元素的全排列。如何避免得到重复的结果呢?可以把相同的元素排一个顺序,只能从左往右按顺序排。
为方便判断,先把 nums 排序(从小到大或者从大到小都可以)。
- 如果 nums[i] != nums[i−1],那么 nums[i] 就是所有等于 nums[i] 的数中的第一个数。我们规定:这种情况可以随意填。
- 如果 nums[i] == nums[i−1],继续讨论:
- 如果 nums[i−1] 没有填入排列,为了避免生成重复的排列,绝对不能填 nums[i],直接 continue。这会导致后续所有等于 nums[i] 的数全部 continue,因为对于后面的 nums[i] 来说,nums[i] 和前面的数 nums[i−1] 相等,并且 nums[i−1] 没有填入排列。
- 如果 nums[i−1] 已经填入排列,那么 nums[i] 是剩余元素(子问题)中的第一个等于 nums[i] 的数,可以随意填。
时间复杂度:O(n⋅n!),其中 n 为 nums 的长度。分析方法同 46 题的题解。
空间复杂度:O(n)。返回值的空间不计入。
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
ranges::sort(nums);
int n = nums.size();
vector<vector<int>> ans;
vector<int> path(n); // 所有排列的长度都是 n
vector<int> on_path(n); // on_path[j] 表示 nums[j] 是否已经填入排列
// i 表示当前要填排列的第几个数
auto dfs = [&](this auto&& dfs, int i) -> void {
if (i == n) { // 填完了
ans.push_back(path);
return;
}
// 枚举 nums[j] 填入 path[i]
for (int j = 0; j < n; j++) {
// 如果 nums[j] 已填入排列,continue
// 如果 nums[j] 和前一个数 nums[j-1] 相等,且 nums[j-1] 没填入排列,continue
if (on_path[j] || j > 0 && nums[j] == nums[j - 1] && !on_path[j - 1]) {
continue;
}
path[i] = nums[j]; // 填入排列
on_path[j] = true; // nums[j] 已填入排列(注意标记的是下标,不是值)
dfs(i + 1); // 填排列的下一个数
on_path[j] = false; // 恢复现场
// 注意 path 无需恢复现场,因为排列长度固定,直接覆盖就行
}
};
dfs(0);
return ans;
}
};