1. 问题描述
给出一组可能包含重复项的数字,返回该组数字的所有排列,结果以字典序 升序 排列。
示例:
输入:
[1,1,2]返回值:
[[1,1,2],[1,2,1],[2,1,1]]
2. 核心逻辑
在处理 [1, 1, 2] 时,由于含有两个 1,如果套用最基础的 DFS 模板,就会产生大量冗余的重复排列。
例如:
- 当我们以第一个 1 为开头,可以得到
[1, 1, 2]和[1, 2, 1]。 - 当我们以第二个 1 为开头,又会得到
[1, 1, 2]和[1, 2, 1]。
这样得到的结果就含有重复项。
要想去除掉重复项,需要按照下面的逻辑进行处理:
- 首先将数组 升序 排列,让相同的数字放在一起。
- 然后,在递归树的同一层,如果 当前的数字和上一个数字相同,且上一个数字已经尝试过了,那么当前这个数字就不应该再作为开头去尝试。
3. 完整C++代码实现
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param num int整型vector
* @return int整型vector<vector<>>
*/
vector<vector<int>> res;
bool used[8] = {false};
vector<int> path;
void dfs(vector<int> &num)
{
if(path.size() == num.size())
{
res.push_back(path);
return;
}
for(int i=0; i<num.size(); i++)
{
if(used[i]) continue;
//注意这里i>0很关键,我们肯定不会在第一次循环就进行剪枝
if(i>0 && num[i] == num[i-1] && !used[i-1]) continue;
used[i] = true;
path.push_back(num[i]);
dfs(num);
path.pop_back();
used[i] = false;
}
}
vector<vector<int> > permuteUnique(vector<int>& num) {
//排序是剪枝的前提
sort(num.begin(),num.end());
dfs(num);
return res;
}
};
4. 程序运行全过程
假设输入数组已排序为 nums = [1, 1, 2],我们来分析一下 DFS 的完整流程,大家可以对照上面的代码看下面的流程分析:
从根节点出发:
-
选择
nums[0],此时used = [T, F, F],push_back之后path = [1]。 -
进入第二层递归:
-
由于
used[0]为true,所以会直接跳过第一轮for循环,选择第二轮for循环的nums[1],此时used = [T, T, F],push_back之后path = [1, 1]。 -
然后进入第三层递归:
- 前两个
used都为true,选择nums[2],此时used = [T, T, T],push_back之后path = [1, 1, 2],这是第一个结果。 - 在尝试进行第四次递归时,此时 已经满足 了
path.size() == num.size(),于是进行 回溯。 - 回到第三层递归,执行
pop_back并把used[2]置位false,当前used = [T, T, F],path = [1, 1]。 - 此时,
for循环达到边界,跳出循环,本次dfs调用结束,进行回溯。
- 前两个
-
回溯到第二层,执行
pop_back并把used[1]置位false,当前used = [T, F, F],path = [1]。 -
然后进行第二层的下一轮
for循环,此时选择nums[2],path = [1, 2]。- 然后再进入第三次递归,选择
nums[1],此时,path = [1, 2, 1]。 - 得到了第二个结果
[1, 2, 1],然后进行回溯。 - 此时,回溯到第二层之后,第二层递归的
for循环也结束了,继续回溯。
- 然后再进入第三次递归,选择
-
程序又回到了根节点:
- 撤销了
num[0],此时used = [F, F, F],path = [],进入根节点的第二轮for循环。 - 准备选择第二个 1 时,触发判断条件
nums[1] == nums[0]且used[0] == false,这意味着nums[0]刚刚作为开头已经完整试过了所有可能性,并退回了。 - 这时执行剪枝,用
continue直接跳过第二个 1 。
收尾:
- 循环进入
i = 2,选择 2 作为开头,此时path = [2]。 - 后续逻辑同上,最终得到
[2, 1, 1]。