携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情
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
解题思路
// 第一种
用回溯解决,与46. 全排列类似,但是nums可以有重复元素。
那么就要在同一层去重,去重条件为当当前元素与前一个元素相同,且前一个元素未在used里标记为true。即前一个元素在同一层被使用过了,并未在上一层被使用。
然后要进行递归,若该元素未被标记为true,则可以进入递归。否则跳过此次循环。
作者:peterz-li
链接:https://leetcode.cn/problems/permutations-ii/solution/47-quan-pai-lie-ii-by-peterz-li-75b3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
// 第二种
全排列就不说了,要去重的功能,其实就是循环的时候,加一个对象缓存一下,下次再遇到这个数,开头的结果就直接略过了
// 第三种
重点在如何去重和剪枝。现在有两个要去重:
选取的元素下标不能重复,保证每个元素都被选到。我这里选择使用 item {set} 来保持下标
每层选取的元素的值不能重复。参考 集合II,使用预排序 + uesd{set} 的方法保证不会出现这种情况:
nums = [2,2],result = [[nums[0], nums[1]], [nums[1], nums[0]]]
代码实现
// 第一种
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permuteUnique = function (nums) {
nums.sort((a, b) => a - b);
const res = [], path = [];
const backtracking = function (used) {
if (path.length === nums.length) {
res.push([...path]);
return;
}
for (let i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] === nums[i - 1] && !used[i - 1]) {
continue;
}
//当数组未被定义时,undefined === false 的结果为false
if (!used[i]) {
used[i] = true;
path.push(nums[i]);
backtracking(used);
path.pop();
used[i] = false;
}
}
}
backtracking([]);
return res;
};
// 第二种
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permuteUnique = function (nums) {
if (nums.length === 1) {
return [nums];
}
let res = [], map = {};
nums.forEach((e, index) => {
if (!map[e]) {
map[e] = 1;
let arr = [...nums];
arr.splice(index, 1);
permuteUnique(arr).forEach((e1) => {
let arr = [e, ...e1];
res.push(arr);
});
}
});
return res;
};
// 第三种
// 相比于全排列问题,这道题从 nums 元素不重复变成了可重复。那我们沿用 集合II 问题的思路,预排序 + used 来处理即可解决每层 for 循环重复选取相同大小元素的问题。
// 其实就是 集合II 问题少了 startIndex 的剪枝 + 全排列保证元素下标不重复。
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permuteUnique = function(nums) {
const result = [], item = new Set() // item 存下标
function backtracking() {
if (item.size >= nums.length) {
result.push([...item].map(numsIndex => nums[numsIndex]))
return
}
let used = new Set()
for (let i = 0; i < nums.length; i++) {
// 保证每层选取的元素大小不重复
if (used.has(nums[i])) {
continue
}
// 保证下标不重复
if (item.has(i)) {
continue
}
used.add(nums[i])
item.add(i)
backtracking()
item.delete(i)
}
}
nums.sort()
backtracking()
return result
};
如果你对这道题目还有疑问的话,可以在评论区进行留言;