切换主题
子集篇
在开始回溯问题前先了解回溯模板(N
叉树的遍历):
JavaScript
const [ret, path] = [[], []]
const dfs = (路径, 选择列表)=>{
if 满足结束条件{
ret.push(路径) // ret.push([...path])
return
}
for 选择 in 选择列表:
做选择 // path.push(路径)
backtrack(路径, 选择列表)
撤销选择 // path.pop()
}
元素无重不可复选
回溯
代码行4中的条件由于path.length
是一定不大于len
的,故可直接写成ret.push([...path])
。
JavaScript
const subsets = nums => {
const [ret, len] = [[], nums.length]
const dfs = (path, idx) => {
if (path.length <= len) {
ret.push([...path])
}
// i = idx 避免重复选择
for (let i = idx; i < len; i++) {
path.push(nums[i])
dfs(path, i + 1) // 不可复选
path.pop()
}
}
dfs([], 0)
return ret
}
时间复杂度:O(n*2^n)
;空间复杂度:O(n)
。
元素重复不可复选
回溯
对原数据原地排序后,跳过重复元素即可!
JavaScript
const subsetsWithDup = nums => {
const [ret, len] = [[], nums.length]
nums.sort((a, b) => a - b) // 排序
const dfs = (path, idx) => {
ret.push([...path])
// i = idx 避免重复选择
for (let i = idx; i < len; i++) {
// 跳过重复元素
if (i > idx && nums[i] === nums[i - 1]) continue
path.push(nums[i])
dfs(path, i + 1) // 不可复选
path.pop()
}
}
dfs([], 0)
return ret
}
时间复杂度:O(n*2^n)
;空间复杂度:O(n)
。
元素重复可复选
JavaScript
const findSubsequences = nums => {
const [ret, len] = [[], nums.length]
const set = new Set()
const dfs = (path, idx) => {
if (path.length > 1) {
const key = path.toString()
if (!set.has(key)) {
ret.push(path.slice())
set.add(key)
}
}
for (let i = idx; i < len; i++) {
const lenP = path.length
const [prev, curr] = [path[lenP - 1], nums[i]]
if (!lenP || curr >= prev) { // 非严格递增
path.push(curr)
dfs(path, i + 1)
path.pop()
}
}
}
dfs([], 0)
return ret
}
时间复杂度:O(n*2^n)
;空间复杂度:O(n)
。