Leetcode 491: 递增子序列
给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况
示例 1:
输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]
示例 2:
输入:nums = [4,4,3,2,1]
输出:[[4,4]]
回溯三部曲
- 递归函数参数
本题求子序列, 很明显当前元素不能重复使用, 需要 start 去控制下一次递归 for循环 的起始位置.
let result = [] // 最终输出的结果
let temp = [] // 记录单次符合的结果
const backtracking = (start)
- 递归终止条件
- 可以不加终止条件, 因为 start 每次都会递归加1, 当超过给出数组长度就不会再进行递归
- 收集结果: 题目中给出至少需要两个元素, 那么当 temp 的长度大于1的时候便推入 result
if(temp.length > 1){
result.push([...temp]) // 拷贝一份
}
- 单层搜索逻辑
- 同一父节点下, 同一层级使用过的元素就不能再使用了 -> 需要每层遍历的时候创建一个 Set, 记录每个元素, 如果相同, 则 continue (Set没有对应的pop是因为新的一层会重新定义, 起到清空效果)
- 递增子序列, 需要验证新元素和是否大于 temp 末尾的元素, 如果新元素更小, 则 continue
if(temp.length > 0 && nums[i] < temp[temp.length - 1] || uset.has(nums[i])) continue
temp.push(nums[i])
uset.add(nums[i])
backtracking(i + 1)
temp.pop()
}
完整代码
var findSubsequences = function(nums) {
const result = []
const temp = []
const backtracking = (start) => {
if(temp.length >= 2){
result.push([...temp])
}
const uset = new Set()
for(let i = start; i < nums.length; i++){
// 情况(||): 可能这一个数字并不比前一个小,但是重复了,需要continue
if(temp.length > 0 && nums[i] < temp[temp.length - 1] || uset.has(nums[i])) continue
temp.push(nums[i])
uset.add(nums[i])
backtracking(i + 1)
temp.pop()
}
}
backtracking(0)
return result
};
Leetcode 46: 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]
回溯三部曲
- 递归函数参数
- 本题不需要 start, 因为集合 [1, 2] 和 [2, 1] 是两个集合, 也就是说1要在两个集合中都使用
- 需要一个 used 数组, 每次推入 temp 集合中的元素下表设置为1, 下次递归的时候便可以排除
const result = []
const temp = []
const used = new Array(nums.length).fill(0)
- 递归终止条件
如果 temp 的数组和 nums 的数组长度一致, 则推入到 result
if(temp.length === nums.length){
result.push([...temp])
}
- 单层搜索逻辑
- 判断如果 used[i] = 1, 表明当前元素已加入到 temp 里, 需要过滤掉
- 添加新元素的时候需要设置 used[i] = 1
for(let i = 0; i < nums.length; i++){
if(used[i] === 1) continue
temp.push(nums[i])
used[i] = 1
backtracking()
used[i] = 0
temp.pop()
}
完整代码
var permute = function(nums) {
const result = []
const temp = []
const used = new Array(nums.length).fill(0)
const backtracking = () => {
if(temp.length === nums.length){
result.push([...temp])
}
for(let i = 0; i < nums.length; i++){
if(used[i] === 1) continue
temp.push(nums[i])
used[i] = 1
backtracking()
used[i] = 0
temp.pop()
}
}
backtracking()
return result
};
Leetcode 46: 全排列 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]]
回溯三部曲
- 递归函数参数
- 需要一个 used 数组, 每次推入 temp 集合中的元素下表设置为1, 下次递归的时候便可以排除
- 每层需要一个 set 来排除同层级使用过的元素
const result = []
const temp = []
const used = new Array(nums.length).fill(0)
- 递归终止条件
如果 temp 的数组和 nums 的数组长度一致, 则推入到 result
if(temp.length === nums.length){
result.push([...temp])
}
- 单层搜索逻辑
- 判断如果 used[i] = 1, 表明当前元素已加入到 temp 里, 需要过滤掉
- 或
- 如果 set 里已经包含这个元素, 则过滤掉
for(let i = 0; i < nums.length; i++){
if(used[i] === 1) continue
temp.push(nums[i])
used[i] = 1
backtracking()
used[i] = 0
temp.pop()
}