回溯算法!!
只要有递归就会有回溯,回溯隐藏在递归函数的下面
回溯算法是回溯搜索算法,是纯暴力的搜索算法
一般用于解决以下问题
- 组合问题:N个数里面按一定规则找出k个数的集合,组合是不强调元素顺序的,排列是强调元素顺序。
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 棋盘问题:N皇后,解数独等等
一般的回溯问题是将问题抽象成一个n叉树进行递归,所以需要递归三部曲,集合的大小就构成了树的宽度,递归的深度就构成了树的深度。
- 确定参数列表返回类型
- 确定终止条件
- 确定单层逻辑
- 模板
void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果} }
组合问题;是无序,不重复的
1.给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。你可以按 任何顺序 返回答案。
如果n=4,k=2。那么可以用简单的两层for循环来解决,可是当n=100,k=50的时候就需要用50层for循环来解决,非常的繁琐
for(int i=1;i<n;i++){for(int j=i+1;j<n;j++){return [i,j];}
}
然后,我们将上述问题抽象成一个树形结构,看出for循环用来横向遍历,递归的过程是纵向遍历。
回溯三部曲
- 确定递归函数参数和返回值:void backpacking(n,k,startindex),然后确定一维数组path,即n叉树中的路径(1,2)、(1,3).....和二维数组result记录所有的path结果,其中参数n,k,startindex,其中startindex是记录开始索引,即每一次从哪一个数开始组合
void backpacking(n,k,startindex)
- 确定终止条件:即每一次递归的结束条件就是到达叶子节点,也就是path.size = k,k就是n叉树的深度
if(path.size == k)result.add(path);
- 确定单层搜索逻辑:每一次的中间节点就是一次for循环,即从startindex开始遍历到n,比如这是startindex = 1,n=4,将1压入,进入回溯
for (int i = startIndex; i <= n; i++) { // 控制树的横向遍历path.push_back(i); // 处理节点backtracking(n, k, i + 1); // 递归:控制树的纵向遍历,注意下一层搜索要从i+1开始path.pop_back(); // 回溯,撤销处理的节点 }
class Solution {List<Integer> path = new ArrayList<>();List<List<Integer>> result = new ArrayList<>();public List<List<Integer>> combine(int n, int k) {backpacking(n,k,1);return result;}void backpacking(int n,int k,int startindex){if(path.size() == k){result.add(new ArrayList<>(path));return;}for(int i = startindex;i<=n;i++){path.add(i);backpacking(n,k,i+1);path.removeLast();}}
}
完整代码如下:其中第一遍的过程是:
1.startindex=1,path.size = 0,进入for循环,i=1,path={1} ,然后进入递归si+1=2,path.size=1,进入for循环,i=2,2入队,path={1,2},进入递归si+1=3,达到终止条件,返回(1,2),这时将2移除path,path={1},i=3,3入队,path={1,3},进入递归si=4,达到结束条件,将(1,3)返回,将3移除,i=4,4入队,path={1,4},进入递归,达到终止条件,返回(1,4),这是退出i=1进行遍历的for循环.。。
2.进入i=2的for循环,即从2开始遍历组合的for循环。。。。
startIndex = 1
└── i = 1 → path = [1]└── i = 2 → path = [1,2] ✅ → result += [1,2]└── i = 3 → path = [1,3] ✅ → result += [1,3]└── i = 4 → path = [1,4] ✅ → result += [1,4]
└── i = 2 → path = [2]└── i = 3 → path = [2,3] ✅ → result += [2,3]└── i = 4 → path = [2,4] ✅ → result += [2,4]
└── i = 3 → path = [3]└── i = 4 → path = [3,4] ✅ → result += [3,4]
└── i = 4 → path = [4]└── i = 5 > n → 不执行