简单递归
小编在初遇剑指offer上的几道递归题时,被折磨的不行不行,想出来后只想大喊一句:这不就是斐波那契数列的递归思想吗!今天小编来分享一下剑指offer上的几道递归题的做题思路,希望能起到点睛之笔之效,点醒你的递归思路
1、一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)
我认为在对递归问题的思考中,最重要的步骤便是大事化小,小事化了,越复杂的问题越要将其拆分成小问题
Math:表示跳法的拆分
Q:假设可爱的小青蛙只需要跳1级台阶
A:显然只有一次跳法
Math:1
Q:假设可爱的小青蛙需要跳2级台阶
A:想一想也不难,可以跳1级再跳1级,又或者直接跳2级
Math:1+1、2
Q:假设可爱的小青蛙需要跳3级台阶,4级台阶,……,n级台阶呢?
A:可能当n还小的时候都难不倒你,但是当n高达100,1000,10000呢?
因此,我们必须找到一个求解的规律,下述将以3级台阶为例
在此之前,我们需要知道的是可爱蛙一次只能上1级或者2级台阶,这决定了可爱蛙从哪级台阶来
1)可以从1级台阶,跳2级到达台阶3
2)可以从2级台阶,跳1级到达台阶3
规律:
为了方便描述,我们引入f(n) → 表示到达第n级阶梯的总方法数
通过分析不同的n,我们不难得到递推关系式:f(n) = f(n-1) + f(n-2)
Q:递推关系式所表达的实际含义到底是什么?
A:它的左式代表到达第n级台阶的总方法数
A:它的右式代表到达第n级台阶的前一个状态要么是在第n-1级,要么是在第n-2级,即跳上第n级台阶,要么是从n-1级跳1级,要么是从n-2级跳2级
其实这里很容易产生一个思维误区:可爱蛙不能从n-2级先跳1级,再跳1级到达n级吗?
我们仍以3级台阶为例,f(3) = f(2) + f(1)
f(2):0→2、0→1→2
f(1):0→1
我们不难发现其实我们的思维误区便是重复思考了问题,所设想的在n-2级先跳1级,再跳1级的情况其实早就已经被n-1级所包含,弄清楚这个问题可以更好的理解接下来的题目
但只有递推公式仍未真正解决问题,我们还需要推断出递归的终止条件
拆分:
在n=3时,f(3) = f(2) + f(1),由关系式得跳上第3级台阶的次数与跳上第1和第2级台阶的次数有关
我们需要思考的问题由跳上第3级台阶 → 如何跳上第2级台阶和第1级台阶
1)可以从0级台阶,跳2级到达台阶2
2)可以从1级台阶,跳1级到达台阶2
1)可以从0级台阶,跳1级到达台阶1
结论:
至此我们得到了f(0) = 1,f(1) = 1,f(2) = 2
根据f(3) = f(1) + f(2) = 3
Q:我们仅仅得到了f(1),f(2)的值吗?
A:当然不是,f(1),f(2)所对应的定值同时是我们递归终止的条件
A:f(4) = f(3) + f(2)
A:f(3) = f(2) + f(1)
A:f(2) = f(1) + f(0)
A:最终我们得到 f(4) = f(1)+f(0)+f(1)+f(1)+f(0),不难发现我们每次的计算过程于n=0,1时终止
f(0)是一个比较特殊的值,独立看f(0)并不具有实际的意义,但若将其放在右式中则表达了从第0级开始的策略,我们只是将其归纳在递推关系中而已,作为递归结束的条件之一
Q:上代码!
A:
int stepI(int n)
{
if (1 == n)
return 1;
if (0 == n)
return 1;
return stepI(n-1)+stepI(n-2);
}
2、一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法
这题是第1题的进阶版,我只想说这只小青蛙确实是过分了,言归正传,该题改变的条件仅是上一次所处的可能状态
Q:假设健壮的小青蛙只需要跳1级台阶
A:显然只有一次跳法
Math:1
Result:1次
Q:假设健壮的小青蛙需要跳2级台阶
A:想一想也不难,可以跳1级再跳1级,又或者直接跳2级
Math:1+1、2
Result:2次
Q:假设健壮的小青蛙需要跳3级台阶
A:与题1相比发生了变化
Math:1+1+1、1+2、2+1、3
Result:4次
Q:假设健壮的小青蛙需要跳4级台阶
A:与题1相比发生了变化
Math:1+1+1+1、1+2+1、2+1+1、3+1、1+1+2、2+2、1+3、4
Result:8次
小编列出结果的次数是希望大家能发现他们的次数有着满足*2的特殊关系,这是一个取巧的方法,我不过多描述,但它确实是递归表达式进行数学化简后的结果
同样,我们必须找到一个求解的规律,下述将以3级台阶为例
在此之前,我们需要知道的是健壮蛙一次能上1级~3级台阶,这决定了健壮蛙从哪级台阶来
1)可以从0级台阶,跳3级到达台阶3
2)可以从1级台阶,跳2级到达台阶3
3)可以从2级台阶,跳1级到达台阶3
规律:
为了方便描述,我们引入f(n) → 表示到达第n级阶梯的总方法数
注意:f(0)与之前一样,它代表直接从0级台阶到所需的n级台阶的策略
通过分析不同的n,我们不难得到递推关系式:f(n) = f(n-1) + f(n-2) + …… + f(0)
但只有递推公式仍未真正解决问题,我们还需要推断出递归的终止条件
拆分:
在n=3时,f(3) = f(2) + f(1) + f(0)
思考的问题由跳上第3级台阶 → 如何跳上第2级台阶、第1级台阶、第0级台阶(默认为1)
1)可以从0级台阶,跳2级到达台阶2
2)可以从1级台阶,跳1级到达台阶2
1)可以从0级台阶,跳1级到达台阶1
1)可以从0级台阶,跳3级到达台阶3
Q:上代码!
A:等等!我先以3级台阶为例列个样板,我认为关键便在于f(n-1)表示健壮蛙跳1级而来的情况,f(n-2)表示健壮蛙跳2级而来的情况
int stepII(int number)
{
int count = 0;
if(number == 0)
return 1;
if(number == 1)
return 1;
for(int i =1; i<=number;++i)
{
count += stepII(number-i);
}
return count;
}
3、我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
小编刚刚看到这题便唰唰的在草稿纸上画方格,漫无目的涂来涂去,倒也是找到了规律,但是其实回过来一想,便能发现玄机
Q:为什么限制了被覆盖矩形的行数为2呢?
A:因为我们使用2*1的小矩形覆盖
Q:2*1小矩阵可以怎么覆盖对方呢?
A:
规律:
当小矩形横着覆盖时会导致它的下半部分也只能横着覆盖
当n=1时,只有1种情况
当n=2时,只有2种情况
当n=3时,横着覆盖则右边形成n=1的情况,而竖着覆盖则右边形成n=2的情况
当n=4时,横着覆盖则右边形成n=2的情况,而竖着覆盖则右边形成n=3的情况
……
总结:
为了方便描述,我们引入f(n) → 表示覆盖2*n大矩形的方法数
由上述不难发现递推关系式:f(n) = f(n-1) + f(n-2)
最让人纠结的可能便是,下述新添加的起始条件
Q:如果我们不是顶格设置小矩形呢,还用考虑第三种情况吗?
A:不知道大家有没有注意到,其实第三种情况已经被包含在竖着覆盖则右边形成n=2的情况中
这题容易产生的思维误区与之前两题是一个性质的,要注意不同策略之间的包含关系
通俗的描述:
顶格的矩形一定是会被覆盖的,而它被覆盖的方式只有2种,要么横着要么竖着
当顶格被设置后继续思考接下来的新顶格如何设置,仔细一想这不正是跳蛙跳阶梯的过程吗?
Q:上代码
A:
int rectCover(int number)
{
if(0 >= number)
return -1;
if(1 == number)
return 1;
if(2 == number)
return 2;
return rectCover(number-1)+rectCover(number-2);
}
总结
上述3题是比较简单的递归,都是同一只披着狼皮的羊,从它们的解题思路中也能看出递归的关键便是寻找递推关系式和递归结束条件。希望我的分享对大家有所帮助,如若有错误的地方请及时指出,感激不尽!