一、DFS与回溯的区别
- DFS和回溯法主要区别在于状态重置,DFS在访问过一个结点之后会标记为访问过,这个标记不会被撤销。回溯法也会对结点进行标记,但是在回溯之后会撤销这个标记。例如寻找条路径,DFS在找到一条后就会结束,回溯法可以将所有的路径找到。
二、何时使用回溯
- 当问题需要 “回头”,以此来查找出所有的解的时候,使用回溯算法。
- 满足结束条件或者发现不是正确路径的时候(走不通),要撤销选择,回退到上一个状态,继续尝试,直到找出所有解为止
三、回溯问题类型
- 组合问题:如何按照一定规则在N个数中找出k个数的集合
- 切割问题:一个字符串按照一定规则切割,有多少种切割方式
- 子集问题:一个N个数的集合种有多少符合条件的子集
- 排列问题:N个数按一定规则排列,有几种排列方式
- 棋盘问题:N皇后、数独等
- 注:组合与排列的区别:组合不强调元素顺序,排列强调元素顺序(组合无序,排序有序)
四、如何理解回溯
- 回溯问题都可以抽象为树形结构
- 回溯法解决的问题都是在集合种递归查找子集,集合的大小构成了数的宽度,递归的深度构成了树的深度
- 递归就要有终止条件,必然是一棵高度有限的树(N叉树)
五、如何写回溯
- 画出递归树,找到状态变量(回溯函数的参数:标识每一层的状态,first)
- 根据题意,确立结束条件
- 找准选择列表(与函数参数相关)
- 判断是否需要剪枝
- 处理节点,做出选择,递归调用,进入下一层
- 撤销选择
六、回溯模板
void backtracing(参数)
{
if(终止条件){
存放结果;
return;
}
for(选择:本层集合中的元素(树节点孩子的数量就是集合的大小)){
if(剪枝条件) //剪枝
continue;
处理节点;
backtracing(路径,选择列表); //递归
回溯,撤销处理结果;
}
}
七、leetcode题目总结
- 子集、组合:子集(78)、子集 II(90)、组合(77)、组合总和(39)、组合总和 II(40)
- 全排列:全排列(46)、全排列 II(47)、字符串的排列(567)、字母大小写全排列(784)
- 搜索:解数独(37)、单词搜索(79)、N皇后(51)、分割回文串(131)、二进制手表(401)
注:子集、组合与排列是不同性质的概念。子集、组合是无关顺序的,而排列是和元素顺序有关的,如 [1,2] 和 [2,1] 是同一个组合(子集),但 [1,2] 和 [2,1] 是两种不一样的排列!!!!因此被分为两类问题
注:搜索问题较困难