赞
踩
回溯法 采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:
与动态规划的区别
共同点
用于求解多阶段决策问题。多阶段决策问题即:
不同点
解决一个回溯问题,实际上就是一个决策树的遍历过程。你只需要思考 3 个问题:
1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。
代码方面,回溯算法的框架:
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」。
例题回溯过程解析:
力扣(LeetCode)78
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
算法
定义一个回溯方法 backtrack(start, track)
,第一个参数为索引start
,第二个参数为当前子集 track
。
如果当前子集构造完成,将它添加到输出集合中。
否则,从start
到 n
遍历索引 i
。
将整数 `nums[i]` 添加到当前子集 `track`。
继续向子集中添加整数:backtrack(i + 1, track)。
从 track 中删除 nums[i] 进行回溯。
vector<vector<int>> res; vector<vector<int>> subsets(vector<int>& nums) { // 记录走过的路径 vector<int> track; backtrack(nums, 0, track); return res; } void backtrack(vector<int>& nums, int start, vector<int>& track) { res.push_back(track); for (int i = start; i < nums.size(); i++) { // 做选择 track.push_back(nums[i]); // 继续选择 backtrack(nums, i + 1, track); // 撤销选择,回溯 track.pop_back(); } }
回溯过程:
.
参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。