回溯的定义
定义:回溯,计算机算法,回溯法也称试探法,它的基本思想是:从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法,就称作“回溯法”。
回溯就是在描述很简单的一个过程:
比如我们国际象棋中,有国王和王后,王后的走法是可以横向、竖向、左右两斜向,所以在考虑如何在一个棋盘上放置n个皇后的时候(且不同皇后之间不能互相攻击到),一个最普遍的方法就是枚举。
当左上角[1,1]位置放皇后的时候,考虑[1,2]可不可以放,再根据[1,2]的结果考虑[1,3]可不可以放。把所有做法都枚举出来,选择其中可以用的方法存入结果集合中,这就叫回溯。上述我们举例的问题就是算法中相当经典的——N皇后问题(我们后期会专门更的)。
所以在放与不放的抉择实际上就是一棵二叉树,一个子树是放、另一个子树是不放,两个子树又分别对应不同的放与不放。当搜索完其中一条路径之后,再退回上一节点,看另一种结果,这个过程叫回溯,也是其名字的来源。(还没看懂的继续往下看,慢慢就明白了)。
也就是说,回溯法的本质实际上是一种全面、深刻的暴力搜索,即:问题的每一种情况都要考虑到。
那么很多小伙伴都会产生一个问题,如果所有情况都考虑到的话,那这样写出来的算法时间复杂度一定相当大吧。事实上确实是这样的,时间复杂度本身确实很大,但是运行时间却不一定很长,因为这里还有回溯算法思想另一个形影不离的要点也可以说是回溯算法的精髓——剪枝。
当前路径显然不满足要求的时候,那就没必要继续往它更离谱的方向进行了,相当于当前节点不满足条件,其子树必然也不满足条件,于是所有子树的分支都不必遍历了,这样的话就节省了很多不必要的时间。
小伙伴一定要注意回溯算法经常用到剪枝函数或者剪枝条件。如若不然,就与普通的暴力搜索区别不大了。
我们可以想象一下,如果对每个结点都考虑0和1两种状态,那么问题的时间复杂度将变成O(2^n),但是实际上正是因为有剪枝函数的存在,很多时候其时间复杂度远远达不到这么大,有些问题通过剪枝函数可以降低到O(n)。
递归三部曲没有学习的同学点击链接咯【递归方法论】递归解题的通用思路:以二叉树最大深度问题为例)
http://mp.weixin.qq.com/s?__biz=Mzg2MjkyMTExNg==&mid=2247483670&idx=1&sn=e5bd38f4e65d6ae1357cdc65e7729274&chksm=ce01c9e5f97640f36b89adeb9a01d090f48a3aab74adf53bc2e04f1b4ac8101b241dbecd0d9a&scene=21#wechat_redirect
for(int i = startIndex; i <= n; i++) {
path.add(i);
backtracing(n