下面我通过讲解一道回溯、递归类型的题目帮助大家快速的理解递归的用法
题目描述:
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。你可以按 任何顺序 返回答案。
题目示例:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
这道题什么意思呢,就是给你一个n,则会生成一个数组为[ 1,2,3,…,n],然后让你输出这个数组的全排列
这道题是一道很典型的回溯与剪枝类型的题目,我们通过下面这个方式 来理解:
我们以n=4,k=3为例,则会形成若干个下图所示的二叉树
我们可以看到我们能够形成的组合则有[ 1,2,3][ 1,2,4][ 1,3,4][ 2,3,4]所以说有效的则是层数为3的树
而我们接下来就是将这些树给构造出来
- n 表示范围为 1…n,balance 表示剩余空间,start 表示开始位置,list 为回溯列表
- 判断 balance == 0,如果为 0 则代表 list 中已经存入 k 个数,拷贝 list 存入结果 ans 中
- 如果不为 0,从 start 位置开始递归调用,现将当前位置数据加入 list 中,并进入下一层,等待返回后将本层加入的数据移除,本质就是树的构造过程
- 循环结束的条件就是我们从1-n全部遍历结束,但是我刚刚说过有效的树只有层数k的树,所以我们剪枝的临界点则为n-balance+1
代码如下:
package com.exercise.leetecode.递归And回溯;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class 组合_77 {
static List<List<Integer>> ans = new ArrayList<>();
public static List<List<Integer>> combine(int n, int k) {
getCombine(n, k, 1, new ArrayList<>());//起始位置从1开始,1,2,...,n
return ans;
}
public static void getCombine(int n, int k, int start, List<Integer> list) {
if (k == 0) {//一旦剩余空间为0,则表示list中已经存放了k个数
ans.add(new ArrayList<>(list));
return;
}
for (int i = start; i <= n - k + 1; i++) {
list.add(i);//开始填充当前位置的元素
getCombine(n, k - 1, i + 1, list);//递归调用,即开始访问下一层
//永远保留起始元素,删除后面不要的元素,执行改语句的条件只有两种:
// 1.在k==0里的return执行完成之后才会执行该语句
//2.每一次的for循环执行结束,比如:i由1通过i++变成2时表示第一次循环结束,则会在循环结束时执行该语句
list.remove(list.size() - 1);
}
}
public static void main(String[] args) {
System.out.println(Arrays.toString(combine(4, 2).toArray()));
}
}