剑指offer 第二版(Python3)--面试题10:斐波那契数列,青蛙跳台阶,青蛙变态跳台阶,矩形覆盖

第2章 面试需要的基础知识

  面试题4:二维数组中的查找
  面试题5:替换空格
  面试题6:从尾到头打印链表
  面试题7:重建二叉树
  面试题9:用两个栈实现队列
  面试题10:斐波那契数列
  面试题11:旋转数组的最小值

第3章 高质量的代码

第4章 解决面试题的思路

第5章 优化时间和空间效率

第6章 面试中的各项能力

第7章 两个面试案例


题目描述
  斐波那契数列:
    F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)
  现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。


解题思路
  暴力法就不介绍了。最常用解法是动态规划,这里递推式已经直接给出,编程时注意存储子问题解即可。
普通版:

class Solution:
    def Fibonacci(self, n):
        # write code here
        if n < 0:
            return None
        if n == 0 or n == 1:
            return n
        f = [0] * (n+1)
        f[0], f[1] = 0, 1
        for i in range(2, n+1):
            f[i] = f[i-1] + f[i-2]
        return f[-1]

  时间复杂度O(n),空间复杂度O(n)。上述解法只能说基本合格,仔细分析存储空间,发现每个值用过一次后就不用了,因此一直保存就显得很浪费,所以可以从减少空间复杂度入手,优化代码。

优化版:

class Solution:
    def Fibonacci(self, n):
        # write code here
        if n < 0:
            return None
        if n == 0 or n == 1:
            return n
        pre, cur = 0, 1
        for i in range(2, n+1):
            temp = pre + cur
            pre = cur
            cur = temp
        return cur

  时间复杂度O(n),空间复杂度O(1)。用pre,cur分别保存f(n-2),f(n-1)的值。实际上可以把temp变量也直接省掉,如下:

class Solution:
    def Fibonacci(self, n):
        # write code here
        if n < 0:
            return None
        if n == 0 or n == 1:
            return n
        pre, cur = 0, 1
        for i in range(2, n+1):
            cur, pre = pre + cur, cur
        return cur

  要注意的是需要考虑负数输入,因为题中只说了n是整数,而斐波那契数列中要求n>=0。


拓展训练

题目一:青蛙跳台阶
牛客网 跳台阶

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

解题思路
  和斐波那契数列一样,只是起始值略有不同。可以自己分析起始值。

实战

class Solution:
    def jumpFloor(self, number):
        # write code here
        if number < 0:
            return None
        if number <= 2:
            return number
        pre, cur = 1, 2
        for i in range(3, number+1):
            cur, pre = cur + pre, cur
        return cur

题目二:青蛙变态跳台阶
牛客网 变态跳台阶

  一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解题思路
动态规划:
  与前面题稍有不同,这里递推式如下:
      f(n)=f(n-1)+f(n-2)+……f(1)
如何保存好等式右边累加值是关键。

class Solution:
    def jumpFloorII(self, number):
        # write code here
        if number < 0:
            return None
        if number <= 1:
            return number
        pre_total = 1
        for i in range(2, number+1):
            cur = pre_total + 1
            pre_total = cur + pre_total
        return cur

  很多题目跳出计算机思维,使用数学方式可以更快得到结论。
数学法一:
  易知 :
      f(n)=f(n-1)+f(n-2)+……f(1)
      f(n-1)=f(n-2)+……f(1)
  两式相减得f(n)=2f(n-1)。这种方法本质上还是动态规划,只不过利用数学方法使得递推式更简单了。代码如下:

class Solution:
    def jumpFloorII(self, number):
        # write code here
        if number < 0:
            return None
        if number <= 1:
            return number
        cur = 1
        for i in range(2, number+1):
            cur *= 2
        return cur

数学法二:
              f(n) = 2n-1

  使用数学归纳法证明,因为:
              f(1) =1 = 20;
              f(2)=2=21;
              f(3)=4=22;
假设 f(n-1)=2n-2成立,因为f(n)=2f(n-1)(前面已证明),所以可得:
              f(n) = 2n-1

class Solution:
    def jumpFloorII(self, number):
        # write code here
        if number <= 0:
            return 0
        return 2**(number-1)

题目三:矩形覆盖
  我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

解题思路
牛客网

  1. n = 1 的时候
    只能横着覆盖,一种
  2. n = 2 的时候
    可以横着和竖着覆盖,两种
  3. n = 3 的时候
    第三级横着覆盖,用了一级,剩下 n = 2,有两种覆盖方法
    第三季竖着覆盖,用了两级,剩下 n = 1,有一种覆盖方法
    总共有 3 种
  4. n = n 的时候
    第 n 级横着覆盖,用了一级,剩下 n = n - 1,所以关注第 n - 1 种有几种覆盖方法
    第 n 级竖着覆盖,用了两级,剩下 n = n - 2,所以关注第 n - 2 种有几种覆盖方法

  实际上还是斐波那契数列,就看能不能从具体问题中抽象出来。

class Solution:
    def rectCover(self, number):
        # write code here
        if number < 0:
            return None
        if number <= 2:
            return number
        cur, pre = 2, 1
        for i in range(3, number+1):
            cur, pre = cur + pre, cur
        return cur
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值