• 剑指offer经典题目整理(二)


    一、斐波那契数列(fib)

    1.链接

    斐波那契数列_牛客题霸_牛客网 (nowcoder.com)

    2.描述

    斐波那契数列就是数列中任意一项数字,都会等于前两项之和,满足f(n) = f(n-1) + f(n-2) 的一个数列,例如:1 1 2 3 5 8 13 21 ... 

    现在题目规定第一项和第二项为1,输入一个整数n,代表第n项,要求返回第n项的结果

    3.思路

    思路一:动态规划

    斐波那契数列是动态规划中最经典简单的一个代表,其中的公式就是动态规划的状态方程,这题也是用来引入简单动态规划问题的一个经典题目

    解决动态规划类的问题,需要三步:

    第一步是设置状态参数n,本题中的状态参数n代表的就是斐波那契数列的第n项

    第二步是根据题目意思去寻找关于n的状态方程,本题的状态方程就是f(n) = f(n-1) + f(n-2)

    第三步是确定初始状态,本题中的初始状态题目给出,f(1) = 1 , f(2) = 1

    确定好这三步后就可以根据状态方程和初始值去写代码了

    思路二:递归与剪枝

    第二种解决的思路,其实是分治思想,利用递归的方式,要想知道f(n),就需要知道f(n-1)和f(n-2) 的结果,要知道f(n-1),就要先知道f(n-2)和f(n-3)... 不断向下递归,直到递归到初始值,就可以返回得到结果,但是这种递归会存在大量的重复计算,由于递归会形成新的栈帧,对内存和计算效率的成本都非常大,因此,通过剪枝的方式去减少重复运算,这里的剪枝,就是指,可以将递归过程中,已经计算好的结果,存入到一个容器中,避免重复运算,这里我选择用map容器去实现剪枝操作

    4.参考代码

    思路一:动态规划

    1. class Solution
    2. {
    3. public:
    4. int Fibonacci(int n)
    5. {
    6. if(n == 1 || n == 2)
    7. {
    8. return 1;
    9. }
    10. vector<int> dp;
    11. dp.reserve(n+1);
    12. dp[0] = 0;
    13. dp[1] = 1;
    14. dp[2] = 1;
    15. for(int i = 3; i<=n;i++)
    16. {
    17. dp[i] = dp[i-1] + dp[i-2];
    18. }
    19. return dp[n];
    20. }
    21. };

    代码分析

    思路二:递归与剪枝

    1. class Solution {
    2. public:
    3. int Fibonacci_recursion(int n, map<int, int>& fib) {
    4. if (n == 1 || n == 2)
    5. {
    6. return 1;
    7. }
    8. int pre = 0;
    9. if (fib.find(n - 1) == fib.end()) //没有记录过
    10. {
    11. pre = Fibonacci_recursion(n - 1, fib);
    12. fib[n - 1] = pre;
    13. }
    14. else
    15. {
    16. pre = fib[n - 1];
    17. }
    18. int ppre = 0;
    19. if (fib.find(n - 2) == fib.end())
    20. {
    21. ppre = Fibonacci_recursion(n - 2, fib);
    22. fib[n - 2] = ppre;
    23. }
    24. else
    25. {
    26. ppre = fib[n - 2];
    27. }
    28. return pre+ppre;
    29. }
    30. int Fibonacci(int n) {
    31. map<int, int> fib;
    32. return Fibonacci_recursion(n, fib);
    33. }
    34. };

    二、青蛙跳台阶

    1.链接

    跳台阶_牛客题霸_牛客网 (nowcoder.com)

    2.描述

    一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

    3.思路

    青蛙跳台阶问题,其实就是斐波那契数列的一个变形,也是动态规划的思路去解决,同样是三步

    第一步状态定义:设跳到第n台阶一共有f(n)种跳法

    第二步状态方程:通过分析,可以发现,第n台阶的跳法,跳到第n阶前,要么在n-1个台阶,要么在n-2个台阶上,第n阶跳法总数会等于n-1台阶的跳法总数+n-2台阶的跳法总数,所以状态方程也就是f(n) = f(n-1) + f(n-2)

    第三步初始状态:f(1) = 1,f(2) = 2

    4.参考代码

    1. class Solution {
    2. public:
    3. int jumpFloor(int number)
    4. {
    5. vector<int> dp;
    6. dp.reserve(number+1);
    7. dp[1] = 1;
    8. dp[2] = 2;
    9. for(int i = 3;i<=number;i++)
    10. {
    11. dp[i] = dp[i-1] + dp[i-2];
    12. }
    13. return dp[number];
    14. }
    15. };

    三、矩形覆盖

    1.链接

    矩形覆盖_牛客题霸_牛客网 (nowcoder.com)

    2.描述

    我们可以用 2*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形,从同一个方向看总共有多少种不同的方法?

    3.思路

    核心还是斐波那契数列的变形,也是采用动态规划的思路

    第一状态定义:用 n 个 2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形,从同一个方向看总共有f(n)种不同的方法

    第二状态方程:每次放方块,实际只有两种放置方式,一种是竖着放(放一个),一种是横着放(放两个),类比与青蛙跳台阶一样的思路,最后放置完n个的状态,要么采用竖着放(剩下一个),要么横着放(剩下两个),而放完n个小方块的方法总数就会是这两种状态的方法总数之和

    即,f(n) = f(n-1) + f(n-2) 

    第三初始参数:f(1)= 1 , f(2) = 2

    4.参考代码

    1. class Solution {
    2. public:
    3. int rectCover(int number) {
    4. vector<int> dp;
    5. dp.reserve(number + 1);
    6. dp[1] = 1;
    7. dp[2] = 2;
    8. for (int i = 3; i <= number; i++) {
    9. dp[i] = dp[i - 1] + dp[i - 2];
    10. }
    11. return dp[number];
    12. }
    13. };

    总结

    本次总结的内容是关于剑指offer中,一些关于斐波那契数列以及其变形的经典题目,也是简单的动态规划题目,题目虽然简单,但动态规划的思想比较难,这里算是一个简单的引入,之后还会更加深入的学习动态规划

  • 相关阅读:
    MySQL软件常见操作
    洛谷P1779 魔鬼杀手
    SQL查询优化---批量数据脚本
    四、RocketMQ发送普通消息、批量消息和延迟消息
    夺旗赛 CTF 六大方向基础工具简介集合
    GPU服务器安装驱动、cuda和cudnn和tensorflow
    2024年1月行车记录仪线上行业分析报告:高端化、智能化趋势已现
    国内优秀的多用户商城系统盘点(2022年整理)
    systemctl 命令设置开机自启动失败
    黑苹果引导介绍篇
  • 原文地址:https://blog.csdn.net/china_chk/article/details/136589355