LeetCode回溯算法基本套路总结
回溯算法并不是高效的算法,其本质就是穷举,在所有的可能性中获取到自己想要的结果
一、回溯算法能够解决的问题
- 组合问题:N个数中按照一定规则找出k个数的集合
- 子集问题:一个N个数的集合中找到符合规则的子集
- 切割问题:一个字符串按照一定规则切割
- 排列问题:N个数按照一定规则全排列
- 棋盘问题:比如N皇后问题
如何理解回溯算法
回溯算法可以将所解决的问题抽象成树形结构,集合的大小构成了树的宽度,递归的深度就是树的深度
基本代码模板
//基本的代码结构
class Solution {
定义返回的结果 res
定义返回结果的路径 path
public 返回类型 主函数名(参数) {
backtrack(参数);
return res;
}
void backtrack(参数){
if(递归结束条件){
存放结果到res;
return;
}
for(横向遍历){
处理;
backtrack(参数);
回溯;
}
}
}
各种类型问题解决方法总结
1、组合问题
1.组合
代表题目:leetcode77:组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例:输入:n=2,k=4 输出[[2,4],[3,4],[2,3],[1,2],[1,3],[1,4]]
提示:
1 <= n <= 20
1 <= k <= n
来源:力扣(LeetCode) 链接:leetcode.cn/problems/co…
代码:
class Solution {
//定义返回结果
public List<List<Integer>> res = new ArrayList();
LinkedList<Integer> path = new LinkedList();
public List<List<Integer>> combine(int n, int k) {
if(n < 1){
return res;
}
backTrack(n,k,list,1);
return res;
}
public void backTrack(int n, int k,int index){
//结束条件,path.size()等于k
if(path.size() == k){
res.add(new ArrayList(path));
return;
}
//循环遍历每一个条件
for(int i = index; i <= n - (k - path.size())+1; i++){
list.add(i); //处理
backTrack(n,k,i+1); //递归调用
list.removeLast(); //回溯
}
}
}
分析:
解题步骤:
1、将问题抽象成树形结构
其中注意限制条件:题目中返回k个数的组合,所以只要路径等于k就结束递归,并将路径添加到结果当中, 而且选用过的元素不能再选择,即不能出现[2,2]或者是[2,1]这样的数。
2、编写代码
2.1定义返回结果
//最终返回结果
public List<List<Integer>> res = new ArrayList();
//记录路径
LinkedList<Integer> path = new LinkedList();
2.2backTrack方法
//因为不能够选取重复元素,所以使用index参数记录每次选取元素开始的地方
public void backTrack(int n, int k,int index){
//结束条件,path.size()等于k
if(path.size() == k){
res.add(new ArrayList(path));
return;
}
//循环遍历每一个条件
for(int i = index; i <= n; i++){
list.add(i); //处理
backTrack(n,k,i+1); //递归调用
list.removeLast(); //回溯
}
}
2.3 判断是否能够剪枝(算法优化)
将循环遍历中i<=n替换成了i<= n - (k - path.size())+1
1、已经选择元素path.size()
2、还需要元素个数k-path.size()
3、在集合n中至多要从该起始位置k-path.size()++1开始遍历
总的意思就是如果剩余元素个数已经不足够再组合成一个k个元素的集合,遍历已经没有意义
//因为不能够选取重复元素,所以使用index参数记录每次选取元素开始的地方
public void backTrack(int n, int k,int index){
//结束条件,path.size()等于k
if(path.size() == k){
res.add(new ArrayList(path));
return;
}
//循环遍历每一个条件,
for(int i = index; i <= n - (k - path.size())+1; i++){
list.add(i); //处理
backTrack(n,k,i+1); //递归调用
list.removeLast(); //回溯
}
}