【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖)

本文主要是介绍【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

力扣对应链接:LCR 126. 斐波那契数 - 力扣(LeetCode)

牛客对应链接:斐波那契数列_牛客题霸_牛客网 (nowcoder.com)

核心考点:空间复杂度,fib 理解,剪枝重复计算。


一、《剑指Offer》内容


二、分析问题 

斐波那契数列是:0 1 1 2 3 5 8 13 21 ...

解题方式很多,有递归方式,也有动归(迭代)方式,但是都是最简单的方式。


三、代码 

1、方法一(迭代)

//力扣AC代码
class Solution {
private:int MOD=1e9+7;
public:int fib(int n) {if(n==0) return 0;int first=1;int second=1;int third=1;while(n>2){third=(first+second)%MOD;first=second;second=third;n--;}return third;}
};//牛客AC代码
class Solution {
public:/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param n int整型 * @return int整型*/int Fibonacci(int n) {int first=1;int second=1;int third=1;while(n>2){third=first+second;first=second;second=third;n--;}return third;}
};

2、方法二(递归 + 剪枝)

直接用最简单的方式因为代码空间复杂度过高,过不了 OJ,所以可以采用 map 进行 “剪枝”。

//力扣AC代码
class Solution {
private:int MOD=1e9+7;unordered_map<int, int> filter;
public:int fib(int n) {if(n==0 || n==1) return n;if(n==2) return 1;int ppre=0;if(filter.find(n-2)==filter.end()){ppre=fib(n-2);filter.insert({n-2, ppre});}elseppre=filter[n-2];int pre=0;if(filter.find(n-1)==filter.end()){pre=fib(n-1);filter.insert({n-1, pre});}elsepre=filter[n-1];return (ppre+pre)%MOD;}
};//牛客AC代码
#include <unordered_map>
class Solution {
private:unordered_map<int, int> filter;
public:/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param n int整型 * @return int整型*/int Fibonacci(int n) {if(n==0 || n==1) return n;if(n==2) return 1;int ppre=0;if(filter.find(n-2)==filter.end()){ppre=Fibonacci(n-2);filter.insert({n-2, ppre});}else ppre=filter[n-2];int pre=0;if(filter.find(n-1)==filter.end()){pre=Fibonacci(n-1);filter.insert({n-1, pre});}else pre=filter[n-1];return ppre+pre;}
};

四、相关错题

【错题集-编程题】Fibonacci数列(Fib 数列)-CSDN博客


五、扩展

在青蛙跳台阶的问题中,如果把条件改成:一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级... ...它也可以跳上 n 级,此时该青蛙跳上一个 n 级的台阶总共有多少种跳法?

可以用数学归纳法证明:f(n) = 2^(n-1)。

力扣对应链接:70. 爬楼梯 - 力扣(LeetCode)

牛客对应链接:跳台阶_牛客题霸_牛客网

核心考点:场景转化模型,模型提取解法,简单 dpfib。


1、分析题目

 动规三步骤:

  1. 定义状态:f(n):青蛙跳上第 n 级台阶的总跳法数。(到了 n,只能是从 n-1 或 n-2 跳过上来)
  2. 编写状态转移方程:f(n) = f(n-1) + f(n-2)。
  3. 设置初始值:f(0) = 1(0 台阶就是起点,到达 0 台阶的方法有一种,就是不跳(这里可能有点奇怪,但是如果方法次数为 0,就说明不可能开始)),f(1) = 1,f(2) = 2。

2、代码

(1)方法一(动态规划)
//时间复杂度: O(n), 空间复杂度: O(N)
//牛客AC代码
class Solution {
public:int jumpFloor(int number) {//dp[n] = dp[n-1]+dp[n-2];int dp[number+1];dp[0] = 1;dp[1] = 1;for(int i = 2; i<= number;i++)dp[i] = dp[i-1] + dp[i-2];return dp[number]; //第number下标,就是第number阶台阶}
};//力扣AC代码
class Solution {
public:int climbStairs(int n) {if(n<=2) return n;vector<int> dp(n+1);dp[0]=1, dp[1]=1, dp[2]=2;for(int i=3; i<=n; i++)dp[i]=dp[i-1]+dp[i-2];return dp[n];}
};

(2)方法一(动态规划 + 优化)
//时间复杂度: O(n), 空间复杂度: O(1)
//牛客AC代码
class Solution {
public:int jumpFloor(int number) {int first=1;      //第一个台阶int second=2;     //第二个台阶int third=number; //等于number直接就考虑了dp[]1]=1 && dp[2]=2的情况while(number>2){third=first+second;first=second;second=third;number--;}return third;}
};//力扣AC代码
class Solution {
public:int climbStairs(int n) {if(n<=2) return n;int dp[3];dp[0]=1, dp[1]=1, dp[2]=2;for(int i=3; i<=n; i++){int sum=dp[1]+dp[2];dp[1]=dp[2];dp[2]=sum;}return dp[2];}
};

六、举一反三

牛客对应链接:矩形覆盖_牛客题霸_牛客网 (nowcoder.com)

核心考点:场景转化成模型,特殊情况分析,简单 dp。


1、分析题目

比如 n = 3 时,2*3 的矩形块有 3 种不同的覆盖方法(从同一个方向看):


那如果是用 8 个 2*1 的小矩形无重叠地覆盖一个 2*8 的大矩形(右图),总共又有多少种方法? 

我们先把 2*8 的覆盖方法记为 f(8)。用第一个 1*2 小矩形去覆盖大矩形的最左边时有两个选择,竖着放或者横着放。当竖着放的时候,右边还剩下 2*7 的区域,这种情形下的覆盖方法记为 f(7)。接下来考虑横着放的情况。当 1*2 的小矩形横着放在左上角的时候,左下角必须和横着放一个 1*2 的小矩形,而在右边还剩下 2*6 的区域,这种情形下的覆盖方法记为 f(6),因此 f(8) = f(7) + f(6)。

这不就是斐波那切数列的问题吗?反思一下,很多问题会包裹很多现实问题,解决问题的第一步往往是从实际问题中提炼出我们的解决问题的数学模型,然后再进行解决。这里也可以使用多种方法解决,不过我们这里重点用 dp,倒也不是说它是最优的,而是平时在写代码的时候,这种思想还是用得少,所以就多写一写。


用 n 个 2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形,每次放置的时候,无非就两种放法,横着放或竖着放。

其中,横着放一个之后,下一个的放法也就确定了,所以虽然放置了两个矩形,但属于同一种放法。其中,竖着放一个之后,本轮放置也就完成了,也属于一种方法。

所以,当 2*n 的大矩形被放满的时候,它无非就是从上面两种放置方法放置来的。

下面继续使用 dp 来进行处理,我们发现斐波那契数列的方式也可以处理,因为前面已经讲过,这里就不再用这种方式来写了

  • 状态定义:f(n) : 用 n 个 2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形所用的总方法数。
  • 状态递推:f(n) = f(n-1)【最后一个竖着放】 + f(n-2)【最后两个横着放】。
  • 初始化:f(0) = 1(f(0) 这里可以不考虑,因为语义不清淅,如果考虑的话就把值设为 1,可参考前面的原因),f(1) = 1,f(2) = 2。

注意:这里需要充分考虑 n 是 [0,1] 时的情况,OJ 一般测试用例设计的比较全面,会有 0,1 传进来,这个时候后续的 dp[1] = 1; 就可能报错。


2、代码

(1)方法一(动态规划)
//牛客AC代码
class Solution {
public:int rectCover(int number) {if(number<=2)return number;int dp[number+1];dp[1]=1, dp[2]=2;for(int i=3; i<=number; i++)dp[i] = dp[i-1]+dp[i-2];return dp[number];}
};

这篇关于【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/941576

相关文章

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

hdu 4565 推倒公式+矩阵快速幂

题意 求下式的值: Sn=⌈ (a+b√)n⌉%m S_n = \lceil\ (a + \sqrt{b}) ^ n \rceil\% m 其中: 0<a,m<215 0< a, m < 2^{15} 0<b,n<231 0 < b, n < 2^{31} (a−1)2<b<a2 (a-1)^2< b < a^2 解析 令: An=(a+b√)n A_n = (a +

最大流=最小割=最小点权覆盖集=sum-最大点权独立集

二分图最小点覆盖和最大独立集都可以转化为最大匹配求解。 在这个基础上,把每个点赋予一个非负的权值,这两个问题就转化为:二分图最小点权覆盖和二分图最大点权独立集。   二分图最小点权覆盖     从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。 建模:     原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t

荣耀嵌入式面试题及参考答案

在项目中是否有使用过实时操作系统? 在我参与的项目中,有使用过实时操作系统。实时操作系统(RTOS)在对时间要求严格的应用场景中具有重要作用。我曾参与的一个工业自动化控制项目就采用了实时操作系统。在这个项目中,需要对多个传感器的数据进行实时采集和处理,并根据采集到的数据及时控制执行机构的动作。实时操作系统能够提供确定性的响应时间,确保关键任务在规定的时间内完成。 使用实时操作系统的

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

POJ3041 最小顶点覆盖

N*N的矩阵,有些格子有物体,每次消除一行或一列,最少要几次消灭完。 行i - >列j 连边,表示(i,j)处有物体,即 边表示 物体。 import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;impo

一些其他面试题

阿里二面:那你来说说定时任务?单机、分布式、调度框架下的定时任务实现是怎么完成的?懵了。。_哔哩哔哩_bilibili 1.定时算法 累加,第二层每一个格子是第一层的总时间400 ms= 20 * 20ms 2.MQ消息丢失 阿里二面:高并发场景下引进消息队列有什么问题?如何保证消息只被消费一次?真是捏了一把汗。。_哔哩哔哩_bilibili 发送消息失败

hdu 6198 dfs枚举找规律+矩阵乘法

number number number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description We define a sequence  F : ⋅   F0=0,F1=1 ; ⋅   Fn=Fn

zookeeper相关面试题

zk的数据同步原理?zk的集群会出现脑裂的问题吗?zk的watch机制实现原理?zk是如何保证一致性的?zk的快速选举leader原理?zk的典型应用场景zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?zk对事物的支持? 1. zk的数据同步原理? zk的数据同步过程中,通过以下三个参数来选择对应的数据同步方式 peerLastZxid:Learner服务器(Follo