算法学习-回溯
算法学习——回溯
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。
在二叉树系列中,我们已经不止一次,提到了回溯,例如二叉树:以为使用了递归,其实还隐藏着回溯 (opens new window)。
回溯是递归的副产品,只要有递归就会有回溯。
一、回溯法的效率
回溯法的性能如何呢,这里要和大家说清楚了,虽然回溯法很难,很不好理解,但是回溯法并不是什么高效的算法。
因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。
那么既然回溯法并不高效为什么还要用它呢?
因为没得选,一些问题能暴力搜出来就不错了,撑死了再剪枝一下,还没有更高效的解法。
此时大家应该好奇了,都什么问题,这么牛逼,只能暴力搜索。
二、回溯法解决的问题
回溯法,一般可以解决如下几种问题:
组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等
三、回溯法模板
回溯法的模板如下:
1 | public void backtrack(参数) { |
四、回溯法中的去重
去重分为树枝去重和树层去重
树枝去重代表在同一个结果中,同一元素不能使用两次
树层去重代表在同一层中,同一元素不能使用两次
比较通用的方法是使用used数组来进行去重
典型例题:Leetcode 47.全排列III
Leetcode 47.全排列III
题目要求
- 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
解法
与Leetcode46类似,区别在于这道题不仅需要树枝去重,还需要树层去重
需要注意的是代码
1 | if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) { |
如果改成used[i - 1] == true也是正确的
这是因为used[i - 1] == true是在树枝上去重,used[i - 1] == false是在树层上去重,树层去重效率更高
![used[i-1]==false树层去重](/image/Java/%E7%AE%97%E6%B3%95Leetcode/Day37_Leetcode47.%E5%85%A8%E6%8E%92%E5%88%97II/%E5%8E%BB%E9%87%8D1.png)
![used[i-1]==true树枝去重](/image/Java/%E7%AE%97%E6%B3%95Leetcode/Day37_Leetcode47.%E5%85%A8%E6%8E%92%E5%88%97II/%E5%8E%BB%E9%87%8D2.png)
1 | class Solution { |
五、总结

图源:代码随想录
