leetcode——回溯法基础

1、回溯法

「回溯法」实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就「回溯」返回,尝试别的路径。
一般情况下,看到题目要求「所有可能的结果」,而不是「结果的个数」,就知道需要暴力搜索所有的可行解了,可以用「回溯法」。

回溯是一种算法思想,递归是一种编程方法,回溯法可以用递归来实现。
回溯法整体思路:搜索每一条路径,每次回溯是对具体的一条路径而言。对当前搜索路径下的未探索区域进行搜索,可能有两种情况:
(1)当前未搜索区域满足结束条件时,保存当前路径并推出当前搜索。
(2)当前未搜索区域需要继续搜索,则遍历当前所有可能的选择;如果该选择符合要求,则把当前选择加入当前的搜索路径中,并继续搜索新的未探索区域。

注意:未搜索区域是指搜索某条路径时的未搜索区域,并不是全局的未搜索区域。

何时使用回溯算法
**当问题需要 “回头”,以此来查找出所有的解的时候,**使用回溯算法。即满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止

2、回溯算法与深度优先遍历

「回溯算法」与「深度优先遍历」都有「不撞南墙不回头」的意思。「回溯算法」强调了「深度优先遍历」思想的用途,用一个 不断变化 的变量,在尝试各种可能的过程中,搜索需要的结果。强调了 回退 操作对于搜索的合理性。而「深度优先遍历」强调一种遍历的思想,与之对应的遍历思想是「广度优先遍历」。

深度优先遍历 是一直往某一个方向搜索,而回溯算法建立在深度优先遍历基础之上的,但不同的是在搜索过程中,达到结束条件后,恢复状态,回溯上一层,再次搜索。因此回溯算法与深度优先遍历 的区别就是有无状态重置

3、搜索与遍历

搜索问题的解,可以通过 遍历 实现。所以很多「回溯算法」可以称为爆搜(暴力解法)。因此回溯算法用于 搜索一个问题的所有的解 ,通过深度优先遍历的思想实现。

4、与动态规划的区别

共同点
用于求解多阶段决策问题。多阶段决策问题即:

求解一个问题分为很多步骤(阶段);
每一个步骤(阶段)可以有多种选择。

不同点
动态规划只需要求我们评估最优解是多少,最优解对应的具体解是什么并不要求。因此很适合应用于评估一个方案的效果;
回溯算法可以搜索得到所有的方案(当然包括最优解),但是本质上它是一种遍历算法,时间复杂度很高。

5、回溯法搜所有可行解模板

回溯法可以抽象为一个树形结构。树的宽度是节点的大小,通常用for循环遍历,树的深度通过递归解决。

思路:
①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
②根据题意,确立结束条件
③找准选择列表(与函数参数相关),与第一步紧密关联※
④判断是否需要剪枝
⑤作出选择,递归调用,进入下一层
⑥撤销选择

res = []
path =[]
def backtrack(未搜索区域, res, path):
	if path 满足条件:  # 终止条件
		res.add(path)  # 深拷贝 树的叶子节点处收集
		# return  # 如果不用继续搜索需要 return
	for 选择 in 未探索区域当前可能的选择:
		if 当前选择符合要求:
			path.add(当前选择)
			backtrack(新的未探索区域, res, path)  # 递归
			path.pop()  # 回溯 撤销选择 

backtrack 的含义是:未探索区域中到达结束条件的所有可能路径,path 变量是保存的是一条路径,res 变量保存的是所有搜索到的路径。所以当「未探索区域满足结束条件」时,需要把 path 放到结果 res 中。
path.pop() 是编程实现上的一个要求,即我们从始至终只用了一个变量 path,所以当对 path 增加一个选择并 backtrack 之后,需要清除 。

DFS 和回溯算法区别
何时使用回溯算法

**当问题需要 “回头”,以此来查找出所有的解的时候,**使用回溯算法。即满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止

原文参考:
https://leetcode-cn.com/problems/subsets/solution/c-zong-jie-liao-hui-su-wen-ti-lei-xing-dai-ni-gao-/
https://leetcodecn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/

你可能感兴趣的:(数据结构,leetcode,算法)