在上一篇【数据结构与算法系列八(递归见面礼)】中,通过两个小案例:
1.求最终推荐人
2.电影院看电影
相信你已经对递归有一个直观的认识了。那么这一篇我们来对递归做一个详细的分析,比如以下这几个问题,如果你都知道,那么恭喜你!你都会抢答了(开个玩笑,这是黑土大爷说的:不是卖车,就是卖拐,对吧)。
言归正传,关于递归,我们只需要搞清楚以下几个问题,就算是完全掌握了。很简单有没有?
#考考你:
1.你知道哪些问题适合用递归解决吗?
2.你知道编写递归代码的关键步骤吗?
案例
递归解决的问题模板
我们说每一种数据结构与算法,都是在特定问题域下的产物;也就是说每一种数据结构与算法,都有它们各自适用的问题场景。
关于递归,结合上一篇:求最终推荐人、电影院看电影案例,我们可以归纳如下:
1.问题可以分解为子问题
简述:
对于一个问题的求解,本身可以被分解为更小的子问题
案例:
电影院案例:我们在哪一排的问题,可以分解为我们的前一排人在哪一排的子问题
2.问题的求解,与分解后的子问题,求解思路一致
简述:
问题本身的求解思路,与分解后的子问题求解思路一致
案例:
电影院案例:求解我们在哪一排的问题思路,与求解我们前一排人在哪一排的思路一致
3.问题的求解,存在终止条件
简述:
注意,这一条很重要,递归一定要有明确的终止条件,否则就等于死循环了
案例:
电影院案例:存在终止条件,如果是第一排,则f(1)=1
编写递归代码关键步骤
知道了递归适合解决的问题,那么在实际软件开发中,我们该如何更有效率的编写递归代码呢?有没有什么套路,或者模板。答案是有。
编写递归代码,有两个关键步骤:
1.找出递推公式
简述:
前面我们说了,递归适合于将问题分解为子问题,然后问题本身的求解,与子问题求解思路一致。你需要仔细琢磨并理解这句话的含义,这句话是精髓。如果你理解了,你会发现这其实是一个重复性的问题。
那么顺着这个思路,我们只需要将子问题,在继续分解问为更小的子问题......一直到子问题不能再分解为止。
这里需要提醒一个常见的思维误区:我们在分解问题的时候,千万不要在大脑里去重现每一个分解步骤,这样容易把自己绕进去:走火入魔。毕竟人类的大脑只适合平铺直叙的思维方式,重复性的东西更适合电脑,对吧。你只需要找出不能再分解的最小子问题,然后解决它就对了。
案例:
电影院案例:f(n)=f(n-1)+1
2.找出终止条件
简述:
关于递归就是一个不断分解子问题,与求解子问题的过程。因此一定要存在明确的终止条件。一定要找到它,不然就死循环了。
案例:
电影院案例:f(1)=1
走台阶案例
我们通过一个稍微复杂些的案例,来巩固3.1与3.2两节的内容。假设有一个n阶的台阶,小明每一步可以走1个台阶,或者走2个台阶。求小明走完台阶,总共有多少种走法?
找出递推公式
我们首先尝试分析,有这么几个前提:
1.总台阶数是n
2.走台阶的方式,一次可以走1个台阶,或者一次可以走2个台阶
那么根据递归求解过程:分解子问题。不过这个案例稍微复杂一些,我们可以假设小明第一步只走1个台阶,是一种走法,即 f(n-1) ;第一步走2个台阶,又是一种走法,即f(n-2) 。
这样一来,我们就可以得出递推公式,走完n个台阶的走法f(n) ,等于第一步走1个台阶的走法f(n-1) ,加上第一步走2个台阶的走法f(n-2) 。即:
#走n个台阶的递推公式
f(n)=f(n-1)+f(n-2)
找出终止条件
寻找终止条件的前提是,假设子问题不能再分解为更小的子问题的时候,有明确的终止条件。我们继续尝试分析:
1.我们假设最后只剩下1个台阶的情况,只有一种走法,即:f(1)=1
2.我们假设最后剩下2个台阶的情况,一次可以走1个台阶,是一种走法;一次可以走2个台阶,又是一种走法。那么剩下2个台阶的走法是:f(2)=2
实现代码
有了递推公式,与终止条件,再来编写递归代码就是水到渠成的事情了,很简单了有没有
/**
* 递归求解走n阶抬价的走法:
* 1.求解递推公式
* 1.1.前提:每次可以走1步台阶,或者2步台阶
* 1.2.第一步走法是关键:
* 1.2.1.如果第一步走1步台阶,则剩下n-1步台阶
* 1.2.2.如果第一步走2步台阶,则剩下n-2步台阶
* 1.3.根据1.2.1与1.2.2得出:
* 1.3.1.递推公式:f(n) = f(n-1) + f(n-2)
* 1.3.2.f(n-1)表示剩下n-1步台阶走法
* 1.3.3.f(n-2)表示剩下n-2步台阶走法
*
* 2.求解终止条件
* 2.1.如果只剩下1步台阶,则f(1)=1
* 2.2.如果剩下2步台阶,则f(2)=2,有两种走法
* @param n
*/
public static int walkingMethod(int n){
// 终止条件f(1)=1
if(n == 1) return 1;
// 终止条件f(2)=2
if(n == 2) return 2;
return walkingMethod(n - 1) + walkingMethod(n -2);
}
讨论分享
#考考你答案:
1.你知道哪些问题适合用递归解决吗?
1.1.如果一个问题,满足三个条件,适合用递归解决
a.问题本身,可以分解为子问题
b.问题本身的求解思路,与分解后的子问题求解思路一致
c.求解问题,本身存在明确的终止条件
2.你知道编写递归代码的关键步骤吗?
2.1.编写递归代码的关键步骤,有两步
a.根据分解后的子问题,找出递推公式
b.当子问题足够小的时候,找出终止条件