【一刷《剑指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});
        }
        else
            ppre=filter[n-2];
        int pre=0;
        if(filter.find(n-1)==filter.end())
        {
            pre=fib(n-1);
            filter.insert({n-1, pre});
        }
        else
            pre=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];
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/575501.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ThingsBoard处理设备上报的属性并转换为可读属性

一、前言 二、案例 1、AI生成JSON数据体 2、将json数据体直接通过遥测topic发送查看效果 3、可查看目前整个数据都在一起 ​编辑 4、配置附规则链路 5、对msg的消息值&#xff0c;进行数据的转换&#xff0c;并从新进行赋值。 6、规则链路关联关系 7、再次通过MQTT发送遥…

618大促有哪些值得买的家居好物?618五款必Buy好物

来了&#xff01;来了&#xff01;万众瞩目的618购物狂欢节即将拉开帷幕&#xff0c;我们的目标清晰而坚定&#xff0c;那就是用最实惠的价格尽情享受购物的乐趣。然而&#xff0c;面对各种纷繁复杂的促销活动和琳琅满目的商品&#xff0c;选择困难症似乎也在悄然滋生。因此&am…

【自定义渲染通道】

自定义渲染通道 2023-09-07 14:58 How to Create Masks With the Custom Depth Buffer Tips - Tricks Unreal Engine.mp4 后期材质ppm_customDepth 要加入通道的物体设置 render customdepth pass postprocessvolue 设置post process materials 为上面的ppm_customDepth 不同…

【信安评估】2024年全国职业院校技能大赛高职组“信息安全管理与评估”安徽省选拔赛赛项规程

培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 极安云科专注于技能提升&#xff0c;赋能 2024年广东省高校的技…

PLL深度解析第一篇——PLL的知识图谱

在硬件电路中&#xff0c;时钟就像心脏一样&#xff0c;在时钟的节拍下&#xff0c;不同的芯片、不同的电路、不同的接口都可以有序的进行工作或者通信&#xff08;类似流水线一样&#xff0c;必须有节奏的运行&#xff09;。 但是在芯片中&#xff0c;不同的模块和接口工作的频…

基于SSM的物业管理系统(含源码+sql+视频导入教程+文档+PPT)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的物业管理系统2拥有三种角色 管理员&#xff1a;用户管理、物业管理、房产信息管理、小区概况管理、开发商管理、收费标准管理、物业公司管理等 物业&#xff1a;住户管理、收费…

C语言求 MD5 值

MD5值常被用于验证数据的完整性&#xff0c;嵌入式开发时经常用到。md5sum命令可以求MD5码&#xff0c;下面介绍如何用C语言实现MD5功能。 一、求字符串MD5值 1、md5sum命令 $ echo -n "12345678" | md5sum //获取"12345678"字符串的md5值 结果&…

1小时学会SpringBoot3+Vue3前后端分离开发

首发于Enaium的个人博客 引言 大家可能刚学会Java和Vue之后都会想下一步是什么&#xff1f;那么就先把SpringBoot和Vue结合起来&#xff0c;做一个前后端分离的项目吧。 准备工作 首先你需要懂得Java和Vue的基础知识&#xff0c;环境这里就不多说了&#xff0c;直接开始。 …

Neo-reGeorg明文流量

Neo-reGeorg 1 同IP对&#xff0c;同一个URI&#xff0c;第一个TCP流是“GET”请求&#xff0c;随后的TCP流请求为“POST”。&#xff08;jsp\jspx\php&#xff09; 2 第一个TCP流中&#xff0c;GET只有一个会话。&#xff08;jsp\jspx\php&#xff09;&#xff0c;响应body79…

stm32HAL库-GPIO

一 什么是 GPIO: GPIO(general porpose intput output), 通用输入输出端口 . 二 我们先认识芯片控制 GPIO 输出控制。 2.1LED 硬件原理如图&#xff1a; 当电流从这根电线流通&#xff0c; LED 亮。当电流不通过这根电线&#xff0c; LED 灭。 上面 PF** &#xff0c;芯片电…

平芯微PW7014中文规格书

产品概述 PW7014 具有前端过电压和过温保护功能。 支持 3V 到 36V 的宽输入电压工作范围。 过压保护阈 值可以外部设置 4V~22V 或采用内部默认 6.1V 设置。 超快的过压保护响应速度能够确保后级电路 的安全。 集成了超低导通阻抗的 nFET 开关&#xff0c; 确保电路系统应用更好…

如何替代传统的方式,提高能源企业敏感文件传输的安全性?

能源行业是一个关键的基础设施领域&#xff0c;它涉及能源的勘探、开采、生产、转换、分配和消费。随着全球经济的发展和人口的增长&#xff0c;能源需求持续上升&#xff0c;这对能源行业的可持续发展提出了挑战。能源行业的传输场景多种多样&#xff0c;需要重点关注能源企业…

性能工具之 JMeter 自定义 Java Sampler 支持国密 SM2 算法

文章目录 一、前言二、加密接口1、什么是SM22、被测接口加密逻辑 三、准备工作四、JMeter 扩展实现步骤1&#xff1a;准备开发环境步骤2&#xff1a;了解实现方法步骤3&#xff1a;runTest 方法步骤4&#xff1a;getDefaultParameters 方法步骤5&#xff1a;setupTest 方法 五、…

3.Docker常用镜像命令和容器命令详解

文章目录 1、Docker镜像命令1.1 获取镜像1.2 查看镜像1.2.1、images命令列出镜像1.2.2、tag命令添加镜像标签1.2.3、inspect命令查看详细信息1.2.4、history命令查看镜像历史 1.3 搜索镜像1.4 删除和清理镜像1.4.1、使用标签删除镜像1.4.2、清理镜像 1.5 创建镜像1.5.1、基于已…

LANGUAGE-DRIVEN SEMANTIC SEGMENTATION

环境不易满足&#xff0c;不建议复现

Google Ads广告为Demand Gen推出生成式AI工具,可自动生成广告图片

谷歌今天宣布在Google Ads广告中为Demand Gen活动推出新的生成人工智能功能。 这些工具由谷歌人工智能提供支持&#xff0c;广告商只需几个步骤即可使用文本提示创建高质量的图片。 这些由人工智能驱动的创意功能旨在增强视觉叙事能力&#xff0c;帮助品牌在YouTube、YouTube…

lesson05:C++内存管理

1.内存分布 2.c中动态内存管理 3.operator new和operator delete函数 4.new和delete实现原理 1.内存分布 1.1常见的内存分布 1.2相关问题 答案&#xff1a;CCCAA AAADAB 我们讲以下易错的部分&#xff1a; 7.数组char2是在栈上开的空间&#xff0c;然后将"a…

主机登录输入正确的密码后也不能正常登录

尝试登录主机发现不能登录&#xff0c;执行journalctl -xe 发现报错fail to start switch root&#xff0c;初步判断是缺少文件bash文件 拷贝文件发现磁盘空间不足&#xff0c;清理日志文件 然后尝试修改密码&#xff1a; 再次尝试登录&#xff0c;发现问题解决&#xff0c;同时…

python获取文件路径

文件&#xff1a;allpath_parameter.py # 获取当前目录路径 # current_dir os.getcwd() # 获取当前目录路径 realpath00 os.path.abspath(os.path.join(os.path.dirname(os.path.split(os.path.realpath(__file__))[0]), .)) print(realpath00)# 获取当前目录的上级目录路…

C++ 并发编程 - 入门

目录 写在前面 并发编程&#xff0c;启动&#xff01; 写在前面 计算机的并发指在单个系统里同时执行多个独立的任务。 在过去计算机内只有一个处理器时并发是通过快速的切换进程上下文所实现的&#xff0c;而现在计算机已经步入了多核并发时代&#xff0c;所以多个进程的并…