剑指offer-第二章算法之斐波拉契数列(青蛙跳台阶)
递归与循环
递归:在一个函数的内部调用这个函数。
本质:把一个问题分解为两个,或者多个小问题(多个小问题相互重叠的部分,会存在重复的计算)
优点:简洁,易于实现。
缺点:时间和空间消耗严重,如果递归调用的层级太多,就会超出栈容量。
循环:通过设置计算的初始值及终止条件,在一个范围内重复运算。
斐波拉契数列
题目一:写一个函数,输入n,求斐波拉契(Fibonacci)数列的第n项,定义如下:
第一种解法:用递归的算法:
long long Fabonacci(unsigned int n) { if(n<=0) return 0; if(n==1) return 1; return Fabonacci(n-1)+Fabonacci(n-2); }
当n=10的时候的调用图如下:
从上图我们可以看到递归的时候,有很多数都被重复计算了,对性能带来极其负面的影响,改算法的时间复杂度为n的指数次方。
第二种解法:用循环(时间复杂度为O(n))
#include <iostream> using namespace std; long long Fabonacci(unsigned int n) { int arrary[2]={0,1}; long long FabN; if(n<2) FabN=arrary[n]; long long FabOne=1; long long FabTwo=0; for(unsigned int i=2;i<=n;++i) { FabN=FabOne+FabTwo; FabTwo=FabOne; FabOne=FabN; } return FabN; } void main() { long long n=Fabonacci(15); cout<<n<<endl; }
java代码:
public class Fabonacci { //斐波拉契数列的非递归的实现,用循环。时间复杂度为O(n) public int fabonacci(int n){ int[] a={0,1}; if(n<2) return a[n]; int FabOne=0; int FabTwo=1; int FabN=0; for(int i=2;i<=n;i++){ FabN=FabOne+FabTwo; FabOne=FabTwo; FabTwo=FabN; } return FabN; } //斐波拉契数列的递归写法 public long fabonacci1(long n){ long fabN=0; if(n<=0) fabN=0; else if(n==1) fabN=1; else fabN=fabonacci1(n-1)+fabonacci1(n-2); return fabN; } public static void main(String[] args){ Fabonacci fab=new Fabonacci(); int f=fab.fabonacci(5); long f1=fab.fabonacci1(5); System.out.println(f+" "+f1); } }
题目二:一只青蛙一次可以跳上一级台阶,也可以跳上2级台阶,求该青蛙跳上n级台阶的共有多少种跳法。
思路:当只有一级台阶的时候,青蛙的跳法也只有一种。当有两级台阶的时候,青蛙的跳法有两种(一是:一下跳两级台阶,二是:一级一级的跳)。当有n级台阶的时候,青蛙在第一次起跳的时候只跳了一级台阶,则还剩下n-1级台阶的跳法,如果在第一次起跳的时候跳了两级台阶,则还剩下n-2级台阶的跳法。整个题目正好是一个斐波拉契数列。公式如下:
题目三:矩阵的覆盖,用八个2*1的小矩阵去覆盖一个2*8的大矩阵。如下图所示:
第一个小矩阵可以有两种覆盖方法横着,那么此时,必须由第二个小矩阵也横着,剩下2*6的大矩阵;竖着,那么还剩下2*7的大矩阵需要覆盖。因此可得:f(8)=f(6)+f(7).
公式同上第二题。