递归算法在我们编程中会经常使用到,我们通过一些例子来详细的认识递归算法。
一:平时我们去电影院看电影 ,电影已经开始,我们想找到自己的位置,可是灯光太暗也看不清座位编号,我们可以问一排的编号,但是他也不知 道自己的排数,你只能不停的往前问,一直到第一排,而后一排就是+1,再一直往后推找到自己的位置,于是我们有
F(n) = F(n-1) + 1;F(1) = 1 ;
这便是标准的递归思想:去的过程就是 ’递‘ 回来的过程为 ‘归’
将上面的公式转换成我们的代码有:
int f(int n) {
if (n == 1) return 1;
return f(n-1) + 1;
}
上面的例子是一个简单的递归问题,对于一般的问题是否可以使用递归求解,一般需要满足以下3个条件:
1:一个问题的解可以分解成几个子问题的的解
何为子问题,即数据规模更加小的问题
2:分解后的子问题,除了数据规模不同,求解的思路完全相同。
3:存在递归终止条件
写出递归算法的重点在哪里呢?我们通过下面的例子来说明
二: 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶
1 阶 + 2 阶
2 阶 + 1 阶
通过题目可以知道 我们每次可以走1个或者2个台阶,假设我们最后一步是走1个台阶,那么剩下的n-1个台阶共有F(n-1)种方案,如果我们最 后一步是走2个台阶,那么剩下的n-2个台阶共有F(n-2)种方案,于是我们有
F(n) = F(n-1) + F(n-2); F(1) = 1 , F(2) = 2;
有了递推公式与初始条件,问题已然解决:
int f(int n) {
if (n == 1) return 1;
if (n == 2) return 2;
return f(n-1) + f(n-2);
}
通过上面的分析我们可以知道,写递归代码的关键就是找到如何将大问题分解为小问题的规律,并且基于此规律写出递推公式,找出终止条件,最后将递推公式和终止条件翻译成代码。只有遇到递推,我们就把他抽象成一个递推公式,不用想一层层的调用关系,不要试图用人脑去分解递归的每个步骤。
三:递推中的注意事项
1:防止递归太深导致栈内存溢出。
在java中没个方法都有一个方法栈用于存储临时变量,当我们递归太深会导致产生大量临时变量从而产生栈内存溢出。
2:防止重复计算。
在爬楼梯的例子中,求解F(n)会求解F(n-1) 与F(n-2) 当我们求解F(n-1)又会求解F(n-2)与F(n-3) 其中F(n-2)被求解了2次,如何避免值的多次的求解呢?我们可以使用散列表存储其值,这样可以避免重复计算。
递归的代码书写的难点在于找出递推公式,找出递推公式还是需要平时多多练习。