关闭

斐波那契数列算法的优化设计

标签: 数据结构
988人阅读 评论(0) 收藏 举报
分类:
  1. 基本概念

    • 数据:信息载体,被计算机识别处理的集合符号
    • 数据元素:数据基本单位
    • 数据对象:具有相同性质的数据元素集合
    • 数据类型:一个值得集合与定义在此集合上的一组操作总称
    • 抽象数据类型:数据结构+定义与此的一组操作
    • 数据结构:相互之间存在一种或多种关系的数据关系集合
  2. 数据结构三要素

    • 逻辑结构:分为线性结构和非线性结构。如下图:

数据机构逻辑分类图

  • 线性表:元素之间只存在一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。
  • 线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构。

  • 集合:同属于一个集合

  • 属性结构:元素存在一对多的关系
  • 图状结构或网状结构:元素存在多对多关系

    • 存储结构:

      顺序存储:相邻元素存储在物理位置上,元素之间关系由存储单元表示。

      优点:可以实现随机存取,元素占用最少空间

      缺点:只能使用相邻一块存储单元,可能产生较多外部碎片

      链式存储:逻辑上相邻元素存储在物理位置上也相邻存储单元里,元素之间由存储单元邻接关系体现。

      优点:充分利用所有存储单元;

      缺点:每个元素因存储指针而占用额外的存储空间,只能实现顺序存取

      索引存储:建立附加索引表;

      优点:检索速度快

      缺点:增加的索引表,占用过多存储空间;修改时,也会话费很多时间
      散列存储:根据元素关键字直接计算出该元素的存储地址,称为Hash存储

    优点:检索,增加和删除结点操作更快;

    缺点:如果散列函数不好则会出现元素存储单元的冲突,解决冲突需要增加时间和空间开销

3.算法的特点
有穷性、确定性、可行性、输入、输出,其含义,你懂得,无需我多言

4.算法效率的度量

  • 时间复杂度

    一个语句频度在该语句在算法中被重复执行次数,所有语句频度之和,记作:T(n) 是算法问题规模N的函数

  • 空间复杂度

    S(n),定义为算法所耗费的存储空间,是问题规模N的函数

思维拓展
1.对于两种不同的数据结构,逻辑结构或物理结构一定不相同吗?

两种不同的数据结构,他们的逻辑结构和物理结构完全有可能相同。比如二叉树和二叉排序树,二叉排序树可以采取二叉树的逻辑表示和存储方式,前者通常用来表示层次关系,而后者通常用来排序和查找。虽然运算都有建立树,插入结点,删除结点和查找结点等,但是对于二叉树和二叉排序树,这些运算定义是不同的,以查找为例,二叉树时间复杂度是O(n),而二叉排序树是O(log2n)

经典题目2:写一个函数,输入n,其斐波那契数列的第n项。

方法1:使用递归解,时间复杂度是n的指数级别

#include<iostream>
#include<stdlib.h>
using namespace std;

int Fibonacci(int n)
{
    if(n<=0)
        return 0;
    if(n==1)
        return 1;
    return Fibonacci(n-1)+Fibonacci(n-2);
}

void main()
{
    int f=Fibonacci(10);
    cout<<f<<endl;
    system("pause");
}

时间复杂度是n的指数级别,随着n的增大,以f(10)为例,f(10)=f(9)+f(8),f(9)=f(8)+f(7)。其中的f(8)就是重复计算的。

方法2:开辟一个长度为(n+1)的数组,时间复杂度为O(n),空间复杂度为O(n)

将这些计算出来的值保存在一个数组arry[n+1]上,这样计算斐波那契数列就相当于是一个填表的过程:

int Fibonacci(int n)
{
    if(n<=0)
        return 0;
    else if(n==1)
        return 1;
    else
    {
        //动态创建一个长度为(n+1)的数组
        int *arr=new int[n+1];
        arr[0]=0;
        arr[1]=1;
        for(int i=2;i<=n;i++)
        {
            arr[i]=arr[i-1]+arr[i-2];
        }
        int result=arr[n];

        delete [] arr;
        return result;
    }
}

注意点:

  1. 因为不知道要求的f(n)中的n有多大,因此不能事先开辟一个数组,需要动态创建数组。而动态数组与数组变量不同,动态分配的数组将一直存在,直到程序显式释放它为止。普通的数组变量,只要出了数组的作用于,其内存会自动释放。

  2. c++提供delete []表达式释放指针所指向的数组空间。delete []
    arry;该语句回收了arr所指向的数组,把相应的内存返回给自由存储区。在关键字delete和指针arr之间的方括号[]是必不可少的:它告诉编译器该指针指向的是自由存储区中的数组,而非单个对象。delete
    arry只释放了arr指针所指向的内存地址,理论上来说会少释放了内存空间,从而产生内存泄露。

    方法3:优化方法2,空间复杂度为O(1),时间复杂度为O(n)

int Fibonacci(int n)
{
    if(n<=0)
        return 0;
    else if(n==1)
        return 1;

    else
    {
        //当n>=2时,初始化pre=f(0)=0,post=f(1)=1,f(n)=0;
        int pre=0;
        int post=1;
        int fn=0;
        //采用循环计算斐波那契数列,通过两个临时变量pre和post保存中间结果,避免重复计算
        for(int i=2;i<=n;i++)
        {
            fn=pre+post;//fn等于其前面两个元素值的和
            //然后让pre和post分别直线他们后面的元素。
            pre=post;
            post=fn;
        }
        return fn;
    }
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    最新评论