Skip to content
本页目录

子集篇

在开始回溯问题前先了解回溯模板N叉树的遍历):

JavaScript
const [ret, path] = [[], []]
const dfs = (路径, 选择列表)=>{
  if 满足结束条件{
    ret.push(路径) // ret.push([...path]) 
    return
  }

  for 选择 in 选择列表:
    做选择 // path.push(路径)
    backtrack(路径, 选择列表)
    撤销选择 // path.pop()
}

元素无重不可复选

剑指 Offer II 079. 所有子集

回溯

代码行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)

元素重复不可复选

90. 子集 II

回溯

对原数据原地排序后,跳过重复元素即可!

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)

元素重复可复选

491. 递增子序列

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)