回溯的基本介绍:
递归和回溯是相辅相成的。有递归就有回溯,一般递归的下面就是回溯
回溯算法其实也算一种暴力解决办法,只不过是将多重for循环变成了递归函数
回溯能解决的问题:
组合问题、切割问题,子集问题,排列问题,棋盘问题
回溯方法的返回值一般是 void ,方法名一般是 backtracking(无所谓用不用),所需要的参数可以一边写代码,需要用到哪个写进参数列表中
回溯一般是要有终止条件的。回溯的一般代码模板:
if (终止条件){ 收集结果; return; } for (集合元素){ 处理结点; 递归; 回溯操作; }
1、题目介绍
给定两个整数
n
和k
,返回范围[1, n]
中所有可能的k
个数的组合。
你可以按 任何顺序 返回答案。
2、思路分析
比如:n = 4 ,k =2可以抽象的看做一棵树
从第一个结点 1 开始,向下搜索子节点, 集合 [1,2] 满足条件,满足条件后要向上回溯到 1 的位置上,当回溯完成后,需要将 2 这个节点在集合中删除掉,来继续存放下一个元素 [1, ],。继续搜索,[1,4] 满足条件,继续回溯........
为什么 2 的子节点中没有 1 呢?
原因就是组合其实是无序的,前面已经求出 [1,2] 这个组合,那么就不需要在求 [2,1] 了
代码:
public class LeetCode_77 {
//保存最终结果的集合
private List<List<Integer>> result = new ArrayList<>();
public static void main(String[] args) {
int n = 4, k = 2;
LeetCode_77 l = new LeetCode_77();
List<List<Integer>> result = l.combine(n, k);
System.out.println(result);
}
private List<List<Integer>> combine(int n, int k) {
//用于保存每种组合
List<Integer> com = new ArrayList<>();
backtracking(n, k, 1, com);
return result;
}
/**
* @param n n
* @param k k
* @param start 开始搜索的位置
* @param com 保存每种组合
*/
private void backtracking(int n, int k, int start, List<Integer> com) {
if (com.size() == k) {
//终止条件,将组合增加到 result 的集合中
result.add(new ArrayList<>(com));
return;
}
//遍历集合中的元素
//n- (k-com.size())+1 剪枝操作
for (int i = start; i <= n- (k-com.size())+1; i++) {
//处理每个节点
com.add(i);
//进行递归处理
backtracking(n, k, i + 1, com);
//回溯操作。每个组合中有 k 个元素。如果不-1的话,会无法增加新的元素,
com.remove(com.size() - 1);
}
}
}