距离上次更新算法知识已经不知道是猴年马月的事情了,今天先把回溯算法的基础理论更新了,然后会接着上次的算法进度接着更新。
进流程
吾日三省吾身
你还记得你的梦想吗
你有努力实现它吗
可以坚持下去吗
省毕学习
注:以下内容参考了《代码随想录》这本书籍
回溯算法理论基础
回溯算法
那么对于刚开始学习算法的同学来说可能还是不知道什么是回溯算法,但是应该是听说过递归,回溯和贪心算法这些东西。
那么什么是回溯算法呢?
答:全称,回溯搜索算法,对,这是一种应用于搜索上的算法
回溯算法是递归的 “ 副产物 ” ,一般来说,只要有递归的过程就会有对应的回溯的过程。
回溯的效率
其实这个问题是比较尴尬的,因为回溯算法的诞生并不是为了提升效率,而是为了完成它搜索的任务,正如他的名字一样。
回溯的本质其实是暴力枚举,然后挨个搜索我们的答案,因此回溯算法的效率并不是很高,但是也可以通过一些剪枝的操作来提升一下效率。但他仍然是一个枚举,枚举大家知道了,那个效率懂的都懂。
针对的问题
回溯所解决的问题都是在集合中递归搜索子集
组合问题:如何按照一定的规则在N个数中找出 k 个数的集合
切割问题:一个字符串按照一定规则切割,有几种切割方式
子集问题:一个N个数的集合中有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列的方式
棋盘问题:N皇后、数独等问题
理解回溯
质疑回溯,理解回溯,成为回溯
其实在学习二叉树章节的时候就回溯和递归就是家常便饭了,几乎每一题都可以遇到,非常的刺激。
如果回溯解决的问题是一个树结构的问题,那么因为回溯所解决的问题都是在集合中递归搜索子集,所以集合的大小就构成了树的宽度,递归的深度就构成了树的深度。
回溯三部曲
和递归一样,回溯还是有属于它自己的三部曲。
(1)确定回溯函数的返回值和参数
返回值:一般是 null(void)
参数:回溯的参数一般都是一遍写逻辑,需要一个参数就添加一个参数即可
public static void backing(参数){
}
(2)确定回溯函数的终止条件
一般来说,回溯用于树结构的题目,那么当到了叶子节点就结束。
if (终止条件){
结果处理;
return;
}
(3)确定回溯搜索的遍历过程
for (类型 选择 : 集合中的元素) {
处理节点;
backing(路径,选择列表);
回溯,返回上一步;
}
for循环的作用就是遍历一整个集合,backing函数就是自己调用自己,实现递归。
for循环 ==> 横向遍历
递归 ==> 纵向遍历
这样横向和纵向都遍历一遍之后,就将其全部遍历完了。
所以回溯算法的整体模版如下
public class BacktrackingExample {
public void backtracking(参数类型 参数) {
// 终止条件
if (终止条件) {
存放结果;
return;
}
for (选择类型 选择 : 本层集合中元素) {
// 处理节点
处理节点;
// 递归
backtracking(路径,选择列表);
// 回溯,撤销处理结果
撤销处理结果;
}
}
public static void main(String[] args) {
// 在这里调用 backtracking 方法,并传入合适的参数
// backtracking(参数值);
}
}
总结
其实后面的题目和递归一样,理解了就理解了(我也不知道我在说什么...)
ヾ( ̄▽ ̄)Bye~Bye~