题目链接:47. 全排列 II
题目描述
给定一个可包含重复数字的序列 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
文章讲解:代码随想录
视频讲解:回溯算法求解全排列,如何去重?| LeetCode:47.全排列 II_哔哩哔哩_bilibili
题解1:回溯法
思路:使用回溯法解决排列问题。
- 递归函数的参数和返回值:首先创建3个变量 res、path 和 set,path 记录遍历的路径,res 记录结果,set 记录元素的访问状态。递归函数的返回值为 void,没有参数。
- 递归函数的终止条件:找到叶子节点,即 path.length 和 nums.length 相同,也就是查找完毕。
- 单层递归的逻辑:使用 for 循环从0开始直到 nums.length 横向遍历,在当前元素没有访问过时递归的向下纵向遍历寻找组合。
- 剪枝:无。
- 去重:① 树枝去重,避免结果中出现相同的元素。② 树层去重,防止结果中出现相同的排列。
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permuteUnique = function(nums) {
const res = []; // 结果数组
const path = []; // 路径
const arr = new Array(nums.length).fill(false); // 用于树枝去重,记录所有元素的访问状态
const backtracking = function () {
if (path.length === nums.length) {
res.push([...path]); // 当路径的长度和 nums 的长度相等时,记录结果并返回
return;
}
const set = new Set(); // 用于树层去重,记录元素的访问状态
for (let i = 0; i < nums.length; i++) {
// 树枝去重和树层去重
if (arr[i] || set.has(nums[i])) {
continue;
}
path.push(nums[i]); // 记录路径
arr[i] = true; // 记录访问状态
set.add(nums[i]); // 记录访问状态
backtracking(); // 向下查找
// 回溯
path.pop();
arr[i] = false;
}
}
backtracking();
return res;
};
分析:时间复杂度为 O(n!),空间复杂度为 O(n)。
收获
排列问题中的去重情况,和组合问题、分割问题、子集问题类似,使用树层去重。