剑指offer---010(斐波那契数列)

更多题目请点链接:《剑指offer》目录索引

问题描述:

写入一个函数,输入n,求斐波那契数列的第n项,斐波那契数列的定义如下:
这里写图片描述

思路:

三种算法:递归,非递归,矩阵
时间复杂度:递归O(2^n),非递归O(n),矩阵O(logn)
算法比较:递归算法效率低,重复计算,容易溢出;非递归算法避免重复计算,比较实用;矩阵算法效率高,但包含生僻的数学算法,不实用

递归算法:

long long  Fib(unsigned int   n)  
{
    if(n<2)
        return n;

    return Fib(n-1)+Fib(n-2);

}

非递归算法:

long long FibNonR(unsigned int  n)
{
    long long  first=1,second=1,result=1;
    if(n==0)
        return 0;

    while (n > 2)
    {
        result = first + second;
        first = second;
        second = result;
        n--;
    }
    return result;

}

矩阵算法:

首先看一个数学公式,根据这个公式,

这里写图片描述

我们计算f(n)时,即计算这里写图片描述即可得到f(n),证明如下:

这里写图片描述
根据以上数学的证明,可知求f(n)相当于n-1个矩阵相乘;但此运算的时间复杂度为O(n),故引入一个公式:

这里写图片描述

从上面公式可看出,n次方转化为n/2次方,n/2次方又转化为n/2/2次方…….. 这样的算法时间复杂度为O(log n)

代码:

Fib.h

#pragma  once


#include<stdio.h>
#include<windows.h>

typedef struct FibArr
{
    long long _a00;
    long long _a01;
    long long _a10;
    long long _a11;
}FibArr;


FibArr MulArr(FibArr arr1, FibArr arr2);
FibArr FibOfLogN(size_t n);
long long FibFn(size_t n);

void Test();

Fib.c


#define _CRT_SECURE_NO_WARNINGS 1

#include "Fib.h"

FibArr ARR = { 1, 1, 1, 0 };

FibArr MulArr(FibArr arr1, FibArr arr2)
{

    //矩阵乘法
    FibArr arr;
    arr._a00 = arr1._a00*arr2._a00 + arr1._a01*arr2._a10;
    arr._a01 = arr1._a00*arr2._a01 + arr1._a01*arr2._a11;
    arr._a10 = arr1._a10*arr2._a00 + arr1._a11*arr2._a10;
    arr._a11 = arr1._a10*arr2._a01 + arr1._a11*arr2._a11;
    return arr;
}

FibArr FibOfLogN(size_t n)
{
    FibArr arr;
    if (n == 1)
    {
        arr = ARR;
    }
    else if (n % 2 == 0)
    {
        //偶数,(A1^(n/2))*(A1^(n/2))
        arr = FibOfLogN(n / 2);
        arr = MulArr(arr, arr);
    }
    else if (n % 2 == 1)
    {
        //奇数,(A1^(n/2)) *(A1^(n/2))* A1
        arr = FibOfLogN((n - 1) / 2);
        arr = MulArr(arr, arr);
        arr = MulArr(arr, ARR);
    }

    return arr;
}

long long FibFn(size_t n)
{
    if (n < 2)
    {
        return n;
    }
    FibArr arr = FibOfLogN(n-1);
    return arr._a00;
}



void Test()
{
        printf("%d  ", FibFn(0));
    printf("%d  ", FibFn(1));
    printf("%d  ", FibFn(2));
    printf("%d  ", FibFn(3));
    printf("%d  ", FibFn(4));
    printf("%d  ", FibFn(5));
    printf("%d  ", FibFn(6));
    printf("%d  ", FibFn(10));
    printf("%d  ", FibFn(30));
    printf("\n");



}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Fib.h"

int main()
{
    Test();
    system("pause");
    return 0;
}

结果:

这里写图片描述

扩展一:

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

思路:

递归子问题:
每跳1级台阶,剩下n-1级,此时求n-1级台阶共有多少种跳法
每次跳台阶时,都要选择跳1级还是2级,直至跳完n级台阶为止
这种选择的跳法实际上就是斐波那契数列的问题

代码:

size_t Jump(size_t n)//递归
{
    if(n<=1)
        return 1;
    if(n==2)
        return 2;
    return Jump(n-1)+Jump(n-2);
}


size_t  FibNonR(size_t  n)//非递归
{
    size_t  first=1,second=1,result=1;
    if(n<=1)
        return 1;
    if(n==2)
        return 2;

    while (n > 2)
    {
        result = first + second;
        first = second;
        second = result;
        n--;
    }
    return result;

}

扩展二:

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

思路:

1.归纳法:
n=0,0种跳法;
n=1,1种跳法;
n=2,2种跳法;
n=3,4种跳法;
………….
n级台阶,2^(n-1)种方法;

代码:

//归纳法
size_t JumpPow(size_t n)
{
    if(n == 0)
    {
        return 0;
    }
    else
    {
        return pow(2,n-1);
    }
}

2.递归法:
0级台阶,没有台阶跳,f(0)=0
1级台阶,只有一级台阶,f(1)=1
2级台阶,可以一次跳1级台阶,一次跳2级台阶,f(2)=f(2-1)+f(2-2)
3级台阶,可以一次跳1级台阶,一次跳2级台阶,在跳一级,或者一次跳3级台阶,f(3)=f(3-1)+f(3-2)+f(3-3)
……………..

共有n-1级台阶时,f(n-1)=f(n-2)+f(n-3)+…….f(1)+f(0)
共有n级台阶时,f(n)=f(n-1)+f(n-2)+f(n-3)+………..+f(1)+f(0),即n>=2时,f(n)=2*f(n-1)
总结为

这里写图片描述

代码:

//递归
size_t JumpR(size_t n)
{
    if(n == 0)
        return 0;
    if(n == 1)
        return 1;

    return 2*JumpR(n-1);
}

扩展三:

我们可以用2x1的小矩形横着或者竖着去覆盖更大的矩形。请问用8个2x1的小矩形无重叠地覆盖在一个2x8的大矩形,总共有多少种方法?

这里写图片描述

思路:

从左向右开始覆盖,第一次有两种选择,横着放和竖着放

这里写图片描述

根据上图,我们可将其转化为斐波那契数列数列问题
f(8)=f(7)+f(6)
f(7)=f(6)+f(5)
f(6=f(5)+f(4)
………..

代码:

//递归
long long CoverR(long long n)
{
    if(n <= 2 )
    {
        return n;
    }
    return Cover(n-1)+Cover(n-2);
}

//非递归
long long CoverNonR(long long  n)
{
    long  long  first=1,second=1,result=1;
    if(n<=1)
        return 1;
    if(n==2)
        return 2;

    while (n > 2)
    {
        result = first + second;
        first = second;
        second = result;
        n--;
    }
    return result;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值