1 题目
https://leetcode-cn.com/problems/permutations-ii/
2 题意
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
3 思路
author's blog == http://www.cnblogs.com/toulanboy/
3.1 全排列思路
我们可以使用深度优先遍历(也就是递归+回溯
)来生成全排列。
深搜思路:
(1)核心逻辑:第k层负责填充该排列的第k个位置的数值。
(2)边界:当k为n时,说明第0
个到第n-1
个位置的数值已经确认,即得到了一个排列。
3.2 去重思路
3.2.1 方案
(1)先全部求出,然后去重。
(2)生成过程中去重。
由于第1种需要先存储所有方案,然后再去重,时间、空间都比第2种要大。故这里展开讨论第2种去重方案。
3.2.2 如何在生成过程中去重?
(1)给输入序列进行升序排序,那么重复的数字都是相邻的。然后开始递归生成。
(2)在生成过程中,要确保相同数字在某个位置只被使用1次。故,在第K层递归中,如果第i个数字可以填进第k个位置,那么需要判断该位置是否使用了重复数字:
- 判断它是否和前一个数字相同。如果和前1个相同,说明这个数字很有可能在这个位置用过了。
- 判断上一个数字是否已经被使用。如果和前1个相同,而且上一个数字已经被使用,说明上一个数字是在前一个位置被使用的,不影响第i个数字在当前位置的填充。否则说明重复填相同数字。
4 代码
//author's blog == http://www.cnblogs.com/toulanboy/
class Solution {
public:
//get_ans()负责生成排列,k代表当前函数正在填充第k个位置的数值
void get_ans(int k, int n, vector<int>& nums, vector<int>& ans, vector<vector<int>>& permute_list, vector<int> &visit){
//若当前的k>=n的位置,说明前面n个(0~n-1)已经填好,得到一个排列情况
if(k >= n){
permute_list.push_back(ans);
return;
}
//否则说明需要对第k个位置进行填充
for(int i=0; i<n; ++i){
if(visit[i] == 0){
//去重:使得相同的数值在一个位置只放置1次
//当前序列是升序的。如果和前1个相同,而且上一个数字已经被使用,说明上一个数字是在第k-1个位置被使用的,不影响第i个数字在第k个位置的填充。否则说明重复填相同数字。
if(i!=0 && nums[i] == nums[i-1] && visit[i-1] == 0){
continue;
}
else{
ans[k]= nums[i];
visit[i] = 1;
get_ans(k+1, n, nums, ans, permute_list, visit);
visit[i] = 0;
}
}
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());//先升序排序(用于去重:相同的数值在一个位置只放置1次,达到去重的目的)
vector<vector<int>> permute_list;
vector<int> visit(nums.size()+1, 0);
vector<int> ans(nums.size(), 0);
get_ans(0, nums.size(), nums, ans, permute_list, visit);
return permute_list;
}
};