浅谈斐波那契数列

浅谈斐波那契数列

斐波那契数列是老生常谈的问题,他也是一个经典的例题,很多应用问题最后殊途同归都能归结到斐波那契数列,我们重新回归浅谈一下斐波那契数列。
一、重识斐波那契数列
所谓斐波那契数列也就是每一项等于前两项之和的数列,它最早来自于一个兔子繁殖的应用问题,是由斐波那契这位人研究的
假设:一对成年兔子每年生一对小兔,小兔一年后成年。提问:一开始有一对小兔,n年后共有多少只兔子?
0 1 1 2 3 5 8 13……
0+1=1
1+1=2
1+2=3
2+3=5
3+5=8
……
得出 f(n)=f(n-1)+f(n-2) n>=2
可以看出,每一个新的f(n),是前两个旧的f(n-1)和f(n-2)之和,由此我们可以直观的写出一个简洁直观的代码
int fib(int n){
return n<=1?1:fib(n-2)+fib(n-1);
}
图片: https://uploader.shimo.im/f/H7yJvqYoSy0dcCx1.jpg
这是一个递归的解法,由图可看出,fib(5)的计算过程是自顶向下的,每执行一层是时间是上一层2倍,得出算法的时间复杂度是指数级别的为O(2^n),当n为5的时候可以看出有很多数据进行了多次的重复计算,可想而知,n大到40的时候,会产生多少的重复子问题。所以我们可以用动态规划的思想对这个问题进行改造,将递归改成迭代:
int fib(int N) {
int A[N+2];
A[0]=0;
A[1]=1;
for(int i=2; i<=N; i++)
A[i]=A[i-1]+A[i-2];
return A[N];
}
图片: https://uploader.shimo.im/f/8rc5tKapDy0hZM1U.jpg
这个算法的时间复杂度是O(n),通过自底向上的方法,结果从下往上一步步递推出来,由图可知,从算法的角度,利用动态规划来计算已经优化到极限了,因为他所有要计算的点只计算了一次,当然关于这个斐波那契数列还有其他经典的求解方式,大家有兴趣的话可以访问链接看一下https://mp.weixin.qq.com/s/3LR-iVC4zgj0tGhZ780PcQ
二、爬楼梯问题
图片: https://uploader.shimo.im/f/REJpNG2VaoEPdjTc.png
看到这个题目,可以画出状态树
图片: https://uploader.shimo.im/f/L098cLNSZSEDl0kt.jpg
假设一共有无五节台阶,利用递归方法,自顶向下,画出递归状态树,可写出代码:
int climbStairs(int n)
{
if (n1 || n2) return n;
return climbStairs(n-1) + climbStairs(n-2);
}
当台阶数到一定的数量为45的时候,LeetCode测试就会显示超时,针对重复计算的问题,题目中两种方法可以爬到楼顶,差一步到楼顶的方法与差两步到楼顶的方法之和就是到楼顶的所有方法,列出f(n)=f(n-1)+f(n-2),与斐波那契数列的方程一样,利用动态规划自底向上运算:
图片: https://uploader.shimo.im/f/axaQssgViPUT1Yt8.jpg
int climbStairs(int n)
{
if(n<=2) return n;
int one_step=2;
int two_step=1;
int all_wags=0;
for(int i=2;i<n;++i)
{
all_wags=one_step+two_step;
two_step=one_step;
one_step=all_wags;
}
return all_wags;
}
这里没有申请数组,利用one_step和two_step代表数组中的i-1项和i-2项,两个变量不断的递推可以得到最终的结果。
三、不同路径
图片: https://uploader.shimo.im/f/w8rJQ9xVl042uAfC.png
不同路径问题,问题要求从start到finish一共有多少种不同的路径,和爬楼梯问题类似,楼梯顶有两种方式走一步或走两步,到达目的地有两种方法,左边到的方法和上面到的方法相加,所以,可列出状态转移方程方程dp[i][j]=dp[i-1][j]+dp[i][j-1],当遇到空地时方法数为0,可列出下面方程:
图片: https://uploader.shimo.im/f/b4kSQEyhRE0hdLVY.png
对于第一个式子,左方和上方都不可达到时为1
对于第二个式子,机器人可以左方到达,上方不可到达,所以和其左方一点的路径相同
对于第三个式子,机器人可以上方到达,左方不可达到所以和其上方一点的路径相同
对于第四个式子,机器人可以从左方或上方到达,因此为这两个方向的路径之和
可写出如下代码
if (1 == obstacleGrid[0][0]) return 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (0 == i && 0 == j) {
dp[i][j] = 1;
} else if (0 == i && j != 0) {
dp[i][j] = (1 == obstacleGrid[i][j] ? 0 : dp[i][j - 1]);
} else if (0 != i && j == 0) { 
dp[i][j] = (1 == obstacleGrid[i][j] ? 0 : dp[i - 1][j]);
} else {
dp[i][j] = (1 == obstacleGrid[i][j] ? 0 : dp[i][j - 1] + dp[i - 1][j]);
}
}
}
四、三角形最小路径和
先来看一道选择题:
图片: https://uploader.shimo.im/f/czqtxcdYA3E6Qjlg.png
这就是上周期中测试的第十题,我们重新来读一下题目,显而易见A是贪心法叙述,反观其他三个,很容易想到他们描述的就是动态规划吧。
图片: https://uploader.shimo.im/f/xZ6LVqqlTFE9eNCY.png
看到这个题目,我第一眼想到的是贪心,第一行2最小,第二行3最小,第三行5最小,第四行1最小,累加之后就是2+3+5+1=11,但如果修改一下数据
[
[2],
[3,1],
[6,5,1]
[1,1,100,100]
]
可以直观的看出走到第三层选择1后,到第四层的时候只能选择100,无法得到全局的最优解,所以贪心法是无效的。
想要找到全局的最优解,必须按路径计算,最下面到最上面求出最小进行累加,列出dp方程 triangle[i][j]+=min(triangle[i+1][j],triangle[i+1][j+1]);最后循环到最开始的位置就是我们需要的值。
class Solution {
public:
int minimumTotal(vector<vector>& triangle) {
for (int i = triangle.size()-2; i >=0; --i)
{
for(int j=0;j<triangle[i].size();++j)
{
triangle[i][j]+=min(triangle[i+1][j],triangle[i+1][j+1]);
}
}
return triangle[0][0];
}
};

原文地址:https://www.cnblogs.com/liugangjiayou/p/12521416.html