简单易懂递归篇:汉诺塔和青蛙跳台阶问题
今日话题:
- Hanoi
- Frog jumping
- 什么是递归? 程序调用自身的编程技巧称为递归(recursion)。递归做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与愿问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量 核心思想:大事化小,小事化了,一步步逼近限制条件。
- 递归的注意事项: 1.不能死递归,都有跳出条件,每次递归都要逼近跳出条件,否则会出现StackOverflow栈溢出 2. 递归层次不能太深
汉诺塔
说到汉诺塔问题,这个可以看成宋丹丹和赵本山小品《钟点工》中说的“把大象放进冰箱一共需要几步的问题”。
既然是递归,肯定要把问题抽象化来看,要有格局
把大象送进冰箱分三部,把汉诺塔从A移动到C也是三步到位 为了方便理解:
- 把冰箱门打开 == 把n-1层从A借助C挪动到B
- 把大象送进去 == A的最下面一层挪动到C
- 把冰箱门关上 == 把B上的n-1层借助A挪动到C
F(1) = 1 ,F(2) = 3, F(3) = 7, F(4) = 15 ... 因此我们可以推算出F(n) = 2n-1,即时间复杂度为O(2n)
代码如下:
#include <stdio.h> #include <math.h> void HanoiTower_recursion(int level, char A, char B, char C){ if(level == 1){ printf("Level : %d , Move from %c to %c ", level, A, C); //限制条件 }else{ HanoiTower_recursion(level-1, A, C, B); // 将A中n-1借助C移动到B printf("Level : %d , Move from %c to %c ", level, A, C); //将A中最大的一个放到C中 HanoiTower_recursion(level-1, B, A, C); //将B中n-1借助A移动到C中 } } int main(){ int level = 0; char A = A; char B = B; char C = C; printf("Please type how high the tower is >"); scanf("%d",&level); HanoiTower_recursion(level, A, B, C); }
青蛙跳台阶
问题:一只青蛙可以跳一层,也可以跳二层,问到达n层有几种跳法? 这个问题和斐波那契问题很像了,我们回忆一下 Fibonacci : 0、1、1、2、3、5、8、13 … 由此推算成:F(0) = 0, F(1) = 1, F(n) = F(n-2)+F(n-1) 其中n>=2
青蛙跳台阶也是如此,我来举个栗子:
一层台阶: 青蛙从0到1过程: [1] 二层台阶:青蛙从0到2 过程:[ [1,1], [2] ] 一层一层跳到终点,也可以直接起飞跳到终点 三层台阶:青蛙从0到3过程:[ [1,1,1], [1,2], [2,1] ] 一层一层跳到终点,也可以先跳一层再跳两层,还可以先跳两层再跳一层到终点 以此类推我们可以得到: F(1) = 1, F(2) = 2, F(3) = F(2)+F(1) = 3, F(4) = F(3) + F(2) = 5 … F(n) = F(n-1) + F(n-2),这特么不就是Fibonacci Formula??
上代码:
#include <stdio.h> int frog_jumping_toTheDestination(int high){ if(high <= 0) return 0; if(high == 1) return 1; if(high == 2) return 2; return frog_jumping_toTheDestination(high-1)+frog_jumping_toTheDestination(high-2); } int main(){ int high = 0; printf("Please input how high the floor is ? >"); scanf("%d",&high); int ret = frog_jumping_toTheDestination(high); printf("Total ways : %d ", ret); }
我们发现一个问题这样是不是有很多计算重复? 假设是五层台阶吧: F(5) = F(4) + F(3) F(4) = F(3) + F(2) F(3) = F(2) + F(1) 这里F(3)和F(2)都调用了两次,台阶越高,重复计算就越多,最好用非递归,斐波那契也是如此。
非递归:
int frog_jumping_toTheDestination(int high){ int firstStep = 1; int secondStep = 2; int closeToDest = 3; if(high<=0) return 0; if(high == 1) return 1; if(high == 2) return 2; int totalWays = 0; while(closeToDest++<=high){ totalWays = firstStep + secondStep; firstStep = secondStep; secondStep = totalWays; } return totalWays; }