斐波那契数列和跳台阶
- 斐波那契数列
- 题目描述
- 斐波那契数列的定义:
- 数据范围:
- 题目要求:
- 输入描述:
- 输出描述:
- 示例
- 示例 1:
- 示例 2:
- 示例 3:
- 解法
- 1. 递归解法
- 代码解释:
- 2. 动态规划解法(自底向上)
- 代码解释:
- 跳台阶问题
- 题目描述
- 示例
- 示例1
- 示例2
- 解题思路
- 方法一:递归
- 方法二:动态规划
- 总结
斐波那契数列
题目描述
大家都知道斐波那契数列,现在要求输入一个正整数 n
,请你输出斐波那契数列的第 n
项。
斐波那契数列的定义:
- $ \text{fib}(x) = 1 \quad \text{(x = 1 or x = 2)} $
- $ \text{fib}(x) = \text{fib}(x-1) + \text{fib}(x-2) \quad \text{(x > 2)} $
数据范围:
$ 1 \leq n \leq 40 $
题目要求:
- 空间复杂度: O ( 1 ) O(1) O(1)
- 时间复杂度: O ( n ) O(n) O(n),但是也可以使用 O ( log n ) O(\log n) O(logn) 的解法。
输入描述:
一个正整数 n
。
输出描述:
输出斐波那契数列的第 n
项。
示例
示例 1:
输入:
4
输出:
3
说明:
根据斐波那契数列的定义可知,fib(1)=1
,fib(2)=1
,fib(3)=fib(3-1)+fib(3-2)=2
,fib(4)=fib(4-1)+fib(4-2)=3
,所以答案为 3。
示例 2:
输入:
1
输出:
1
示例 3:
输入:
2
输出:
1
解法
1. 递归解法
递归的做法是基于斐波那契数列的定义,逐步向下调用。时间复杂度是 O( 2 n 2^n 2n),并且会大量重复计算,所以效率较低。
int Fibonacci(int n) {if (n <= 2)return 1;else return Fibonacci(n - 1) + Fibonacci(n - 2);
}
代码解释:
if (n <= 2)
: 递归的终止条件,当n
为 1 或 2 时,返回 1。else return Fibonacci(n - 1) + Fibonacci(n - 2)
: 如果n
大于 2,则根据斐波那契数列的定义,递归计算fib(n-1)
和fib(n-2)
,并返回它们的和。
2. 动态规划解法(自底向上)
为了解决递归解法中重复计算的问题,我们可以使用动态规划的方式来从底到顶逐步计算每一项斐波那契数,并记录下来,避免重复计算。
int Fibonacci(int n) {if (n <= 2) return 1;int prev1 = 1, prev2 = 1, current;for (int i = 3; i <= n; ++i) {current = prev1 + prev2;prev2 = prev1;prev1 = current;}return current;
}
代码解释:
if (n <= 2) return 1;
: 若n
为 1 或 2,直接返回 1。int prev1 = 1, prev2 = 1, current;
: 使用三个变量prev1
、prev2
和current
来保存斐波那契数列的前两项和当前项。for (int i = 3; i <= n; ++i)
: 从第三项开始循环计算,逐步得到下一个斐波那契数。current = prev1 + prev2;
: 当前项是前两项的和。prev2 = prev1; prev1 = current;
: 更新prev1
和prev2
为当前项和前两项。
跳台阶问题
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
数据范围:
1 ≤ n ≤ 40
要求:
时间复杂度:O(n)
空间复杂度:O(1)
示例
示例1
输入:
2
返回值:
2
说明:
青蛙要跳上两级台阶有两种跳法,分别是:先跳一级,再跳一级或者直接跳两级。因此答案为2。
示例2
输入:
7
返回值:
21
解题思路
方法一:递归
递归的思路是,青蛙跳上第n级台阶的跳法等于跳上第n-1级台阶的跳法加上跳上第n-2级台阶的跳法。即:
f(n) = f(n-1) + f(n-2)
代码实现:
#include <stdio.h>int jumpFloor(int number) {if (number == 0 || number == 1) {return 1;} else {return jumpFloor(number - 1) + jumpFloor(number - 2);}
}
时间复杂度:
O(2^n),因为每次递归调用会产生两个新的递归调用,导致指数级的时间复杂度。
空间复杂度:
O(n),递归调用栈的深度为n。
方法二:动态规划
为了优化递归方法的时间复杂度,我们可以使用动态规划来避免重复计算。通过保存中间结果,可以将时间复杂度降低到O(n)。
代码实现:
#include <stdio.h>int jumpFloor(int number) {int a = 1;int b = 1;int c = 1;if (number == 0 || number == 1) {return 1;}for (int i = 1; i < number; i++) {a = b;b = c;c = a + b;}return c;
}
时间复杂度:
O(n),只需要遍历一次即可计算出结果。
空间复杂度:
O(1),只使用了常数个额外空间。
总结
- 递归方法 简单直观,但时间复杂度较高,适合小规模问题。
- 动态规划方法 通过保存中间结果,大大降低了时间复杂度,适合大规模问题。