回溯法也可以叫做回溯搜索法,是一种搜索的方式,回溯和递归是相辅相成的,回溯是递归的副产品,只要有递归就会有回溯,所以可以简单的理解回溯函数和递归函数是同一个函数。

大名鼎鼎的回溯法虽然很不好理解,但其本质就是暴力查找,穷举所有可能,然后找出我们想要的答案,并不是什么高效的算法,虽然有些可以剪枝一下,没有更优化的方法了,至于为什么不高效还要用,那没别的更好的了能解决问题就不错了还想咋滴。

回溯法可以解决组合、排列、切割、子集、棋盘(N皇后,解数独)等问题,其中组合无序,排列有序。以上这几类问题都不简单。

回溯法解决的所有问题其实都可以抽象为树形结构,因为它解决的都是在结合中查找子集,集合的大小就构成了树的宽度,用for循环来实现,递归的深度构成树的深度(递归就要有终止条件,必然是一个棵高度有限的N叉树)。

和递归函数一样,三部曲:(1)确定回溯函数返回值和参数(一般不需要返回值,参数其实也不好确定,可以先写逻辑,再定参数)(2)终止条件(3)回溯搜索的遍历过程逻辑。

回溯法的问题有一个套用的模板帮助解决问题:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

终止条件说的就是一旦满足了要求的条件(一般来说搜索到叶子结点),就是找到了满足条件的一条答案,把这个答案存放起来,并接受这层递归。for循环是树的横向遍历,而递归调用时树的深度遍历,这样就能把一棵树都遍历到了。

子集问题收集结果是所有节点。