LeetCode 509: 斐波那契数


斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你 n ,请计算 F(n) 。

示例 1:

输入:2
输出:1

解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2:

输入:3
输出:2

解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例 3:

输入:4
输出:3

解释:F(4) = F(3) + F(2) = 2 + 1 = 3

提示:

  • 0 <= n <= 30

分析

这是一道递推逻辑的题目,既然已经知道了初始值,那么如果就可以使用递归一层一层的返回以达到求取结果的目的.

实现

int fib(int n) {
	if (n == 0) return 0;
	if (n == 1) return 1;
	return fib(n-1) + fib(n-2);
}

这种递归的实现方式在有些时候很有用,例如树的遍历等。但是缺陷也很明显:

  • 由于执行的函数需要不停的压栈保存现场,因此在递归层数比较多时需要占用较多内存,容易造成内存溢出;
  • 递归是函数调用自身,而函数调用是有时间和空间的消耗的;每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间;
  • 递归中的很多计算是重复的,例如示例中的 f i b ( n − 1 ) fib(n-1) fib(n1) f i b ( n − 2 ) fib(n-2) fib(n2)最终都会需要计算 f i b ( 0 ) + f i b ( 1 ) fib(0) + fib(1) fib(0)+fib(1);
  • 递归在很多时候阅读起来并不容易。

所以在压栈层数比较多时,应该尽可能规避使用递归算法.

算法复杂度

时间复杂度

从分析可以知道, f i b ( n ) = f i b ( n − 1 ) + f i b ( n − 1 ) fib(n)=fib(n-1)+fib(n-1) fib(n)=fib(n1)+fib(n1),而 f i b ( n − 1 ) = f i b ( n − 2 ) + f i b ( n − 3 ) fib(n-1)=fib(n-2)+fib(n-3) fib(n1)=fib(n2)+fib(n3)等,即每一个 f i b fib fib都会分解出两个子运算的和,呈现幂增长趋势,这里可以有两种理解方式:

  • 解出通项公式
    在这里插入图片描述
    可知 f ( n ) f(n) f(n)是关于常数的 n n n次幂
  • 借助满二叉树的节点数
    顶层是f(n),在n减小到一定的成都之前每层翻倍,则所有子节点的和约为2^n级别,但不会满二叉,所以比这小点,而满二叉树的节点的数为 2 n − 1 2^n - 1 2n1,所以时间复杂度为 O ( 2 n ) O(2^n) O(2n).

空间复杂度

时间复杂度为 O ( 1 ) O(1) O(1)

其他算法

  • 使用数组保存每个 f ( n ) f(n) f(n)的值:
int fib(int n) {
int fib(int n) {
	int *result = malloc(sizeof(int) * (n + 1));
	memset(result, 0, sizeof(int) * (n + 1));
   int a[2] = {0, 1};
   memcpy(result, a, (n >=1 ? 2 : n)  * sizeof(int));
	for (int index = 2; index <= n; index++) {
		result[index] = result[index - 1] + result[index - 2]; 
	}
	return result[n];
}

算法的时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n).

而事实上,数组保存所有的 f ( n ) f(n) f(n)并没有太大的意义,只需要保存前两项就可以了,其余的空间是不需要的,所以算法可以针对空间复杂度优化一下:

int fib(int n) {
	int a[2] = {0, 1};
	if (n < 2) {
		return a[n];
	}
	for (int index = 2; index < n; index++) {
		a[1] = a[0] + a[1];
		a[0] = a[1] - a[0];
	}
	return a[0] + a[1];
}

这样时间复杂度依然是 O ( n ) O(n) O(n),但是空间复杂度却可以降低为 O ( 1 ) O(1) O(1).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值