给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。
示例 :
输入: nums = [1,2,3]
输出: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
典型的 回溯 题
回溯法采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现,现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。回溯法通常用最简单的 递归 方法来实现
var permute = function (nums) {
let res = []
backtrack(nums, [], res)
return res
};
// 回溯
let backtrack = (nums, arr, res) => {
//当用来存放结果的数组 arr 和 nums长度相同时,说明已经是一个全排列了,放入 res 结果数组中存放
// 这里要记住不能直接 res.push(arr) 因为复杂类型的变量指向的是内存中的地址
if (arr.length === nums.length) {
res.push([...arr])
return
}
for (let i = 0; i < nums.length; i++) {
// 因为原数组不包含重复值,所以在遍历的时候跳过已经放入arr中的值就好了
if(arr.indexOf(nums[i])!==-1) continue
// 将当前值加入到 arr 中,剩下的工作交给递归函数完成
arr.push(nums[i])
backtrack(nums, arr, res)
// 一轮回溯结束后,取消上一步再寻找其他答案
arr.pop()
}
}
这道题和 LCR 084. 全排列 II 之间只有一个条件的变化,就是数组是可能存在重复值的数组
给定一个 可包含重复数 字的整数集合 nums ,按任意顺序 返回它所有不重复的全排列。
所以大体的思路还是相同的,不同的在于if(arr.indexOf(nums[i])!==-1) continue 这部分逻辑的处理
不能简单的通过arr中是否存在 nums[i] 来判断了
上一道题中,其实我们也可以通过设置一个flag数组的方式来实现类似 if(arr.indexOf(nums[i])!==-1) continue 的效果,其本质就是访问过的值就跳过
那么这种思路的实现为
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function (nums) {
let res = []
nums.sort((a, b) => a - b)
// 通过一个值全部为 false 长度为 nums.length 的数组 在回溯过程中访问过的值 flag[i] 设为true 在一次回溯完成之后,再将flag[i]设为false
let flag = new Array(nums.length).fill(false)
backtrack(nums, [], res, flag)
console.log(flag)
return res
}
let backtrack = (nums, arr, res, flag) => {
if (arr.length === nums.length) {
res.push([...arr])
return
}
for (let i = 0; i < nums.length; i++) {
// 如果 flag[i]为true,说明访问过了,跳过
if (flag[i]) continue
flag[i] = true
// 将当前值加入到 arr 中,剩下的工作交给递归函数完成
arr.push(nums[i])
backtrack(nums, arr, res, flag)
// 一轮回溯结束后,取消上一步再寻找其他答案
arr.pop()
flag[i] = false
}
};
在此基础上,我们要实现将相同的值只进行一次回溯,只需要实现在有序数组中,相邻的相同值中之访问第一个,其他的跳过,这个逻辑可以通过下列方法实现
i > 0 && nums[i] === nums[i - 1] && !flag[i - 1])
完整的实现为
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permuteUnique = function (nums) {
let res = []
nums.sort((a, b) => a - b)
let flag = new Array(nums.length).fill(false)
backtrack(nums, [], res, flag)
console.log(flag)
return res
}
let backtrack = (nums, arr, res, flag) => {
if (arr.length === nums.length) {
res.push([...arr])
return
}
for (let i = 0; i < nums.length; i++) {
// 剪枝逻辑
if (flag[i]||(i > 0 && nums[i] === nums[i - 1] && !flag[i - 1])) continue
flag[i] = true
// 将当前值加入到 arr 中,剩下的工作交给递归函数完成
arr.push(nums[i])
backtrack(nums, arr, res, flag)
// 一轮回溯结束后,取消上一步再寻找其他答案
arr.pop()
flag[i] = false
}
};