「代码随想录」回溯算法精讲
该pdf是公众号「代码随想录」回溯算法专题的⽂章整理⽽来,共5万字,包含了30多张树形结构图、15
道⼒扣精选回溯题⽬,21篇回溯法精讲⽂章,由浅⼊深,⼀⽓呵成,这是全⽹最强回溯算法讲解!
本pdf题⽬⼤纲如下:
跟进最新⽂章,关注微信公众号「代码随想录」,你会发现相⻅恨晚!
很多⼩伙伴刷题的时候⾯对⼒扣上近两千道题⽬,感觉⽆从下⼿,我花费半年时间整理了Github项⽬:
「⼒扣刷题攻略」https://github.com/youngyangyang04/leetcode-master。 ⾥⾯有100多道经典算法
题⽬刷题顺序、配有40w字的详细图解,常⽤算法模板总结,以及难点视频讲解,按照list⼀道⼀道刷就
可以了!star⽀持⼀波吧!
公众号:代码随想录
B站:代码随想录
Github:leetcode-master
知乎:代码随想录
后续将在公众号陆续发布其他算法专题的pdf版本,敬请期待!
关于回溯算法,你该了解这些!
代码随想录出品:B站回溯算法视频讲解
什么是回溯法
回溯法也可以叫做回溯搜索法,它是⼀种搜索的⽅式。
在⼆叉树系列中,我们已经不⽌⼀次,提到了回溯,例如⼆叉树:以为使⽤了递归,其实还隐藏着回
溯。
回溯是递归的副产品,只要有递归就会有回溯。
所以以下讲解中,回溯函数也就是递归函数,指的都是⼀个函数。
回溯法的效率
回溯法的性能如何呢,这⾥要和⼤家说清楚了,虽然回溯法很难,很不好理解,但是回溯法并不是什么
⾼效的算法。
因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法⾼效⼀些,可以加
⼀些剪枝的操作,但也改不了回溯法就是穷举的本质。
那么既然回溯法并不⾼效为什么还要⽤它呢?
因为没得选,⼀些问题能暴⼒搜出来就不错了,撑死了再剪枝⼀下,还没有更⾼效的解法。
此时⼤家应该好奇了,都什么问题,这么⽜逼,只能暴⼒搜索。
回溯法解决的问题
回溯法,⼀般可以解决如下⼏种问题:
组合问题:N个数⾥⾯按⼀定规则找出k个数的集合
切割问题:⼀个字符串按⼀定规则有⼏种切割⽅式
⼦集问题:⼀个N个数的集合⾥有多少符合条件的⼦集
排列问题:N个数按⼀定规则全排列,有⼏种排列⽅式
棋盘问题:N皇后,解数独等等
相信⼤家看着这些之后会发现,每个问题,都不简单!
另外,会有⼀些同学可能分不清什么是组合,什么是排列?
组合是不强调元素顺序的,排列是强调元素顺序。
例如:{1, 2} 和 {2, 1} 在组合上,就是⼀个集合,因为不强调顺序,⽽要是排列的话,{1, 2} 和 {2, 1} 就
是两个集合了。
记住组合⽆序,排列有序,就可以了。
如何理解回溯法
回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!
因为回溯法解决的都是在集合中递归查找⼦集,集合的⼤⼩就构成了树的宽度,递归的深度,都构成的
树的深度。
递归就要有终⽌条件,所以必然是⼀颗⾼度有限的树(N叉树)。
这块可能初学者还不太理解,后⾯的回溯算法解决的所有题⽬中,我都会强调这⼀点并画图举相应的例
⼦,现在有⼀个印象就⾏。
回溯法模板
这⾥给出Carl总结的回溯算法模板。
在讲⼆叉树的递归中我们说了递归三部曲,这⾥我再给⼤家列出回溯三部曲。
回溯函数模板返回值以及参数
在回溯算法中,我的习惯是函数起名字为backtracking,这个起名⼤家随意。
回溯算法中函数返回值⼀般为void。
再来看⼀下参数,因为回溯算法需要的参数可不像⼆叉树递归的时候那么容易⼀次性确定下来,所以⼀
般是先写逻辑,然后需要什么参数,就填什么参数。
但后⾯的回溯题⽬的讲解中,为了⽅便⼤家理解,我在⼀开始就帮⼤家把参数确定下来。
回溯函数伪代码如下:
回溯函数终⽌条件
既然是树形结构,那么我们在讲解⼆叉树的递归的时候,就知道遍历树形结构⼀定要有终⽌条件。
所以回溯也有要终⽌条件。
什么时候达到了终⽌条件,树中就可以看出,⼀般来说搜到叶⼦节点了,也就找到了满⾜条件的⼀条答
案,把这个答案存放起来,并结束本层递归。
所以回溯函数终⽌条件伪代码如下:
回溯搜索的遍历过程
在上⾯我们提到了,回溯法⼀般是在集合中递归搜索,集合的⼤⼩构成了树的宽度,递归的深度构成的
树的深度。
如图:
注意图中,我特意举例集合⼤⼩和孩⼦的数量是相等的!
回溯函数遍历过程伪代码如下:
void backtracking(参数)
if (终⽌条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩⼦的数量就是集合的⼤⼩)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
for循环就是遍历集合区间,可以理解⼀个节点有多少个孩⼦,这个for循环就执⾏多少次。
backtracking这⾥⾃⼰调⽤⾃⼰,实现递归。
⼤家可以从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这
棵树全遍历完了,⼀般来说,搜索叶⼦节点就是找的其中⼀个结果了。
分析完过程,回溯算法模板框架如下:
这份模板很重要,后⾯做回溯法的题⽬都靠它了!
如果从来没有学过回溯算法的录友们,看到这⾥会有点懵,后⾯开始讲解具体题⽬的时候就会好⼀些
了,已经做过回溯法题⽬的录友,看到这⾥应该会感同身受了。
总结
本篇我们讲解了,什么是回溯算法,知道了回溯和递归是相辅相成的。
接着提到了回溯法的效率,回溯法其实就是暴⼒查找,并不是什么⾼效的算法。
然后列出了回溯法可以解决⼏类问题,可以看出每⼀类问题都不简单。
最后我们讲到回溯法解决的问题都可以抽象为树形结构(N叉树),并给出了回溯法的模板。
今天是回溯算法的第⼀天,按照惯例Carl都是先概述⼀波,然后在开始讲解具体题⽬,没有接触过回溯
法的同学刚学起来有点看不懂很正常,后⾯和具体题⽬结合起来会好⼀些。
相信很多⼩伙伴刷题的时候⾯对⼒扣上近两千道题⽬,感觉⽆从下⼿,我花费半年时间整理了
Github项⽬:「⼒扣刷题攻略」https://github.com/youngyangyang04/leetcode-master。
⾥⾯有100多道经典算法题⽬刷题顺序、配有40w字的详细图解,常⽤算法模板总结,以及难点视
频讲解,按照list⼀道⼀道刷就可以了!star⽀持⼀波吧!
组合问题
第77题. 组合
void backtracking(参数) {
if (终⽌条件) {
存放结果;
return;
}
!
for (选择:本层集合中元素(树中节点孩⼦的数量就是集合的⼤⼩)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
!
评论2