剑指Offer学习总结-斐波那契数列
2018-01-17 12:02
330 查看
剑指Offer学习总结-斐波那契数列
本系列为剑指Offer学习总结,主要是代码案例的分析和实现:书籍链接:http://product.dangdang.com/24242724.html
原作者博客:http://zhedahht.blog.163.com/blog/static/254111742011101624433132/
原作者博客链接有完整的项目代码下载。
斐波那契数列
题目
题目:写一个函数,输入 n, 求斐波那契 ( Fibonacci ) 数列的第 n 项。斐波那契数列定义如下:(原书的写法少了n==2的情况)
f(n)=⎧⎩⎨⎪⎪0,1,f(n−1)+f(n−2),n=0n=1n>=2
最常见的解法:
最常见的解法是递归的解法,递归的思路的关键1.找到递归的边界条件
2.找到问题缩减的方向
3.找到前后之间的递推公式
这几条上边的公式都给的很明确了。我们可以动手来写代码。
long long Fibonacci_Solution1(unsigned int n) { //边界条件 if(n <= 0) return 0; if(n == 1) return 1; //递推公式 以及规模缩减 return Fibonacci_Solution1(n - 1) + Fibonacci_Solution1(n - 2); }
简单分析一下,这种递归的解法是最简单直接的。但是可以发现计算的过程中,我们会有大量的重复计算,浪费了性能。
下图假设用参数10来举例分析,可以看到有很多重复的子问题计算。
改进的解法:
如何避免重复计算已经计算过的结果,我们可以采用两个变量来保存之前计算过的结果,f(n-1)和f(n-2).具体需要多少个变量来存储迭代,需要具体分析具体分析,通常我们需要看递推公式来判断。
接下来我们需要将递归转换为循环,请记住二者的转换并不一定需要借助栈来进行。
判断是够需要利用栈,首先判断是否递归需要进行回溯操作。
具体关于递归和循环的转换,我们以后可以在进行讨论分析。
从下往上计算, 首先根据f(0)和 f(1)算出 f(2),再根据f(l)和 f(2)算出 f(3)……
依此类推就可以算出第 n 项了。 很容易理解, 这种思路的时间复杂度是O(n)
long long Fibonacci(unsigned n) { int result[2] = {0 , 1}; if(n < 2) return result ; long long fibMinusOne = 1;//n-1 long long fibMinusTwo = 0;//n-2 for(unsigned int i = 2 ; i <= n ; ++i) { fibN = fibMinusOne + fibMinusTwo; fibMinusTwo = fibMinusOne; fibMinusOne = fibN; } return fibN; }
类似的问题
其他类似的问题,我们还有青蛙跳台阶问题,人爬梯子问题,2X1格子覆盖2Xn格子的问题。接下来我们一一分析
青蛙跳台阶问题,一只青蛙一次可以跳上 1 级台阶, 也可以跳上 2 级 ,求 该 靑蛙跳上一个 n 级的台阶总共有多少种跳法
首先考虑最简单的情况,
1级台阶,1种跳法
2级台阶,2种跳法
再来讨论一般情况
我们把 n 级台阶时的跳法看成是 n 的函数,记为 f(n)。
当 n>2 时, 第一次跳的时候就有两种不同的选择:
一是第一次只跳 1 级, 此时跳法数目等于后面剩下的 n-1 级台阶的跳法数0, 即为f(n-1) 注意子问题分解的结果合并是乘法不是加法。
另外一种选择是第一次跳 2 级, 此时跳法数目等于后面剩下的 n-2 级台阶的跳法数目, 即为 f(n-2),
因此 n 级台阶的+同跳法的总数 f(n)=f(n-1)+f(n-2),可以看出这是一个明显的斐波那契数列,
但是终止条件稍有区别。终止条件是n==1和n==2。或者n==0的时候,f(0)=0
人爬梯子的问题和青蛙跳台阶一样,就不做过多的重复了。分析问题的时候,想一想思路分析的过程中,有没有重复的情况。
2X1格子覆盖2Xn格子的问题
我们可以用 2x1的小矩形横着或者竖着去覆盖更大的矩形,请问用 8 个 2X1 的小矩形无重叠地覆盖个 2X8的大矩形, 总共有多少种方法?
我们先把以8 的覆盖方法记为 f(8)。 用第一个 2X1 小矩形去覆盖大矩形的最左边时有两个选择, 竖着放或者横着放。
当竖着放的时候, 右边还剩下 2X7 的区域, 这种情形下的覆盖方法记为 f(7)。
接下来考虑横着放的情况。
当1x2小矩形横着放在左上角的时候, 左下角必须和横着放个 1x2 的小矩形, 而在右边还还剩下 2x6 的区域, 这种情形下的覆盖方法记为 f(6),
因此 f(8)= f(7)+f(6) 此时我们可以看出, 这仍然是斐波那契数列。
但是终止条件稍有区别。
当覆盖的格子只有2X1,有一种办法
当覆盖的格子有2X2,有二种办法
public class Solution { public int RectCover(int target) { if(target==0){ return 0; } if (target == 1) { return 1; } if (target == 2) { return 2; } return RectCover(target - 1) + RectCover(target - 2); } }
相关文章推荐
- 剑指Offer学习总结-栈的压入、 弹出序列
- 剑指Offer学习总结-两个链表的第一个公共节点
- 剑指Offer学习总结-和为S的连续正数序列
- 剑指Offer学习总结-不能被继承的类
- 剑指Offer学习总结-二进制中1的个数
- 剑指Offer学习总结-连续子数组的最大和
- 剑指Offer学习总结-旋转数组的最小数字
- 剑指Offer学习总结-不用加减乘除做加法
- 剑指Offer学习总结-二叉搜索树与双向链表
- 剑指Offer学习总结-实现Singleton模式
- 剑指Offer学习总结-用两个队列实现栈
- 剑指Offer学习总结-最小的k个数
- 剑指Offer学习总结-扑克牌的顺子
- 剑指Offer学习总结-在O(1)时间删除链表结点
- 剑指Offer学习总结-树的子结构
- 剑指Offer学习总结-第3章 高质量的代码
- 剑指Offer学习总结-顺时针打印矩阵
- 剑指offer——斐波那契数列相关问题总结
- 剑指Offer学习总结-和为S的两个数字
- 剑指Offer学习总结-替换空格