⑮ 算法设计思想之“回溯算法”

一、理论

1. 简介

  • 回溯算法是 算法设计 中的一种方法
  • 回溯算法是一种 渐进式 寻找并构建问题解决方法的策略
  • 回溯算法会先从一个可能的动作开始解决问题,如果不行就回溯并选择另一个动作,直到将问题解决

2. 什么问题适合用回溯算法解决?

  • 有很多路
  • 这些路里,有 死路, 也有 出路
  • 通常需要递归来模拟所有路

3. 全排列

输入: [1, 2, 3]
输出: [ [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1] ]

  • 用递归模拟出所有情况
  • 遇到包含重复元素的情况,就回溯
  • 收集所有到达递归终点的情况,并返回

二、刷题

1. 全排列(46)

1.1 题目描述

  • 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案

1.2 解题思路

输入:nums = [1,2,3]
输出:[ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]

  • 要求:1、所有排列情况;2、没有重复情况
  • 有出路、有死路
  • 考虑使用回溯算法

1.3 解题步骤

  • 用递归模拟出所有情况
  • 遇到包含重复元素的情况,就回溯
  • 收集所有到达递归终点的情况,并返回
function permute(nums) {
  const res = [];
  const backtrack = path => {
    if(path.length === nums.length) {
      res.push(path)
    }
    nums.forEach(n => {
      if(path.includes(n)) { return; }
      backtrack(path.concat(n))
    })
  };
  backtrack([]);
  return res;
}

1.4 时间复杂度&空间复杂度

  • 时间复杂度:O(n!)
  • 空间复杂度:O(n)

2. 子集(78)

2.1 题目描述

  • 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)
  • 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集

2.2 解题思路

输入:nums = [1,2,3]
输出:[ [], [1],[2],[1,2],[3],[1,3],[2,3],[1,2,3] ]

  • 要求:1、所有子集;2、没有重复元素
  • 有出路,有死路

2.3 解题步骤

  • 用递归模拟出所有情况
  • 保证接的数字都是后面的数字
  • 收集所有到达递归终点的情况,并返回
function subsets(nums) {
  const res = [];
  const backtrack = (path, l, start) => {
    if(path.length === l) {
      res.push(path);
      return;
    }
    for(let i = start; i < nums.length; i++) {
      backtrack(path.concat(nums[i]), l, i+1)
    }
  };
  for(let i = 0; i <= nums.length; i++) {
    backtrack([], i, 0)
  }
  return res;
}

2.4 时间复杂度&空间复杂度

  • 时间复杂度:O(2^n)
  • 空间复杂度:O(n)

三、总计 -- 技术要点

  • 回溯算法是 算法设计 中的一种方法
  • 回溯算法是一种 渐进式 寻找并构建问题解决方法的策略
  • 回溯算法会先从一个可能的动作开始解决问题,如果不行就回溯并选择另一个动作,直到将问题解决

什么问题适合用回溯算法?

  • 有很多路
  • 这些路里,有 死路, 也有 出路
  • 通常需要递归来模拟所有路

适合回溯算法解决的问题

  • 全排列
  • 子集
posted on 2022-02-10 16:28  pleaseAnswer  阅读(54)  评论(0编辑  收藏  举报