题目:[1]
给定一个可包含重复数字的序列,返回所有不重复的全排列。
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
抛砖引玉
在全排列中,处理过对一个数组的元素重新进行排列,但是全排列中限制了没有重复的元素:全排列[2]
本题中包含重复的元素,则在枚举的过程中,即使从不同位置取元素组合,也可能形成相同的组合。
那么本题的重点就成了如何避免形成重复的组合了:
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permuteUnique = function(nums) {
let _result = [],
inList = new Map()
function helper(item) {
if (item.length === nums.length) {
_result.push([...item])
return
}
for (let i = 0; i < nums.length; i++) {
// 增加重复元素判断,如果元素与上一个相邻元素相同,那么这两个相邻元素只有一个可以参与位置的选择-回溯
if (inList.has(i) || (i > 0 && nums[i] === nums[i - 1] && !inList.has(i-1))) continue
item.push(nums[i])
inList.set(i, true)
helper(item)
// 回溯-释放上面被在该位置选择的元素尝试在该位置选择其他元素
item.pop()
inList.delete(i)
}
}
if (nums.length < 2) return nums.length === 1 ? [nums]: _result
// 排序是重复元素相邻
nums.sort((a, b) => a - b)
helper([])
return _result
}
注意: 上面交换重复的逻辑至少针对一轮交换,即针对一个索引位置的交换,更新要交换的位置后,去重的逻辑需要重新开始
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(nums) {
let _result = []
if (nums.length < 2) return nums.length === 1 ? [nums]: _result
function helper(index) {
if (index === nums.length) {
_result.push([...nums])
return
}
let map = new Map();
for (let i = index; i < nums.length; i++) {
// 一轮交换(一个新位置)中只有未交换过的元素才能参与交换
if(!map.has(nums[i])) {
map.set(nums[i], true);
// 逐个与当前索引位元素交换
swap(nums, i, index)
// 进行后续交换
helper(index + 1)
// 回溯上一次交换,让下一个位置的交换在上一次未交换的基础上开始
swap(nums, i, index)
}
}
}
// 替换指定位置两个数
function swap(a, i, j) {
var tmp = a[i]
a[i] = a[j]
a[j] = tmp
}
helper(0)
return _result
}