回溯与DFS的关键区别在于 DFS会一直深入直到路径可走时结束 而回溯在满足要求后会返回。简而言之就是回溯会回头 DFS 不会。
回溯的代码框架
backtrack(路径,选择列表,最终结果){
//在这块 应该判断我们选择的这一步是否符合要求 比如是否越界,是否已经访问过等 如果不满足则先执行return。
//其次 判断是否达到目的 如果满足 则将改路径加入到最终结果中 然后return
for(选择 :选择列表){
track.add(选择);
backtrack(....);
track.remove(选择);
}
}
大致的回溯函数就是如此, 在LeetCode 上关于回溯有两大类种题型 第一种是概念题 第二种是应用题。
所谓概念题就是考察一些基础 比如 Leecode 上 全排列 1 2,子集 1 2,组合, 组合总和 1-4 。这些题目的考察基本是考察如何去重。下面我总结三种方法用于应对 排列 组合 以及子集
1.visited 数组 visited[i] = true: 第i 个已经被访问过了。(用于排列问题 防止已经加入路径的元素再次被加入到路径中)。
2.backtrack(start, …) 参数列表中加入选择列表的起始点 start, for循环从 start 开始进行选择,(用于求解子集问题,)
3. 如果 选择列表中有重复的元素 则首先对选择列表进行排序 然后在for循环中加入 if(i>0 && nums[i]==nums[i-1]) continue; 从而进行剪枝 。
这三种方法 1,3可以结合用于排列去重,2,3也可以结合用于子集去重 但 1,2 是不可结合使用的。
回溯最难的也是最有意思的应该是应用题,LeetCode 上 有 目标和,单词搜索 1, 单词搜索2(会超时,但是方法是可行的),单词拆分,解数独,N皇后,跳跃游戏3。
DFS的一些题目与回溯很类似, 例如岛屿问题等。