⑮ 算法设计思想之“回溯算法”
一、理论
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)
三、总计 -- 技术要点
- 回溯算法是 算法设计 中的一种方法
- 回溯算法是一种 渐进式 寻找并构建问题解决方法的策略
- 回溯算法会先从一个可能的动作开始解决问题,如果不行就回溯并选择另一个动作,直到将问题解决
什么问题适合用回溯算法?
- 有很多路
- 这些路里,有 死路, 也有 出路
- 通常需要递归来模拟所有路
适合回溯算法解决的问题
- 全排列
- 子集
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)