程序调用自身的编程技巧称为递归。
举例说明一下:
public void Recursion(int n){
if(n>0){
Recursion(n-1);
printf("%d\n",n);
}
}
当n=2的时候首先执行Recursion(2),接着执行Recursion(1),最后结果就是1和2。
递归的调用实质就像是一个堆栈,当我们执行Recursion(2)的时候发现无法得到结果并且接着执行Recursion(1),此时就相当于把Recursion(2)存入栈中,然后执行完Recursion(1)的再从栈中取出Recursion(2)执行得到结果。
如果是for循环加上递归呢
public void Recursion(int n){
for(int i=1;i<=3;i++)
{
if(n>0)
{
Recursion(n-1);
printf("%d\n",n);
}
}
}
对于这种问题,我个人认为一个很好的办法就是去分层理解。
Recursion(2)-> 输出次数:3^1
调用3次Recursion(1)
输出三次2
每一次的Recursion(1)-> 输出次数:3^2
调用3次Recursion(0)
输出三次1
输出结果为111211121112
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
分析:
首先可以看出是一个组合问题
对于例题来看
1.如果组合里有 1 ,那么需要在 [2, 3, 4] 里再找 1 个数;
2.如果组合里有 2 ,那么需要在 [3, 4] 里再找 1数。注意:这里不能再考虑 1,因为包含 1 的组合,在第 1 种情况中已经包含。
所以这里使用回溯算法,首先画出递归树
根据树分析我们可以先在一个循环中依次取出1.2.3.4
然后通过递归操作继续取数,只需注意在取下一个数时要舍去不需要的数
public class Solution {
public List> combine(int n, int k) {
List> res = new ArrayList<>();
if (k <= 0 || n < k) {
return res;
}
// 我们取数是从1开始取
Deque path = new ArrayDeque<>();
dfs(n, k, 1, path, res);
return res;
}
private void dfs(int n, int k, int begin, Deque path, List> res) {
// 当path的值等于k时说明当前以及取满递归结束
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
// 从1开始取数
for (int i = begin; i <= n; i++) {
// 将取得的第一个数存入path
path.addLast(i);
// 开始第二轮取数,取数从i+1开始取
dfs(n, k, i + 1, path, res);
// 假如当我们取得第一组数1.2时后第一层递归结束,开始下一层获取3来组成1.3。但是此时path里面以及有1.2所以要删除2这个数据。
path.removeLast();
}
}
}
初步完成后发现代码可以进行优化(剪枝操作)
例如这个立体中当我们取的第一个数为4的时候以及没有意义了,因为第二个数无论取什么都已经重复了。
继续分析:如果n=8,k=5时,从5开始搜索就没有意义了,因为把5选上后面6,7,8也组不成5个数了。
所以:
当path.size()==1时,还需要选4个数,最大搜索起点为5
当path.size()==2时,还需要选3个数,最大搜索起点为6
当path.size()==3时,还需要选2个数,最大搜索起点为7
当path.size()==4时,还需要选1个数,最大搜索起点为8
根据上面可以看出n = 还需要选择的个数 + 最大搜索起点 + 1
所以我们可以把循环的上界限制为n - 还需选择的个数 + 1
即 for循环修改为i < n - (k - path.size()) + 1;
for (int i = begin; i <= n - (k - path.size()) + 1; i++) {
// 将取得的第一个数存入path
path.addLast(i);
// 开始第二轮取数,取数从i+1开始取
dfs(n, k, i + 1, path, res);
// 假如当我们取得第一组数1.2时后第一层递归结束,开始下一层获取3来组成1.3。但是此时path里面以及有1.2所以要删除2这个数据。
path.removeLast();
}