• [从今天开始修炼数据结构]基本概念


    [从今天开始修炼数据结构]基本概念

    [从今天开始修炼数据结构]线性表及其实现以及实现有Itertor的ArrayList和LinkedList

    [从今天开始修炼数据结构]栈、斐波那契数列、逆波兰四则运算的实现

    [从今天开始修炼数据结构]队列、循环队列、PriorityQueue的原理及实现

    从双十一低价购入了一批书,拖到今天还没开始看,实在不该不该。所以我决定从今天开始修炼数据结构和算法,打下坚实基础!

    学习路线:《大话数据结构》程杰 和 《算法》Robert Sedgewick 两本书对照学习。 再加上网上搜集的优秀博文的参考。代码实现使用Java,写下此篇博文作为记录。   —— 2019.11.25  8:49在B326实验室。

          另外,这两本书缺少动态规划这一部分,如果有幸有人看到我这篇随笔,请推荐你觉得优秀的动态规划教程给我~

    下面开始正式内容

    一、什么是数据结构

      学习数据结构,首先要搞清楚什么是数据,数据相关的概念。

        数据就是计算机可以识别、操作的符号集合。  

        数据元素是组成数据的、有一定意义的基本单位。 也被称为记录。

        数据项:一个数据元素可以由若干个数据项组成。  比如人这个数据元素,可以有眼睛、耳朵、鼻子等若干数据项。

        数据对象是性质相同的数据元素的集合。 比如所有的人,都具有眼睛、耳朵、鼻子,那么人的集合就是数据对象。

      数据结构:相互之间存在一种或多种特定关系的数据元素的集合。 编写一个好的程序,要分析、组织、处理对象的特性和处理对象之间的关系。

      数据结构分为 逻辑结构 和 物理结构。

        逻辑结构:集合结构  线性结构  树形结构  图形结构

        物理结构:顺序存储  链式存储

      在高级语言中,数据类型可以分为 原子类型(不可分解的基本类型) 和结构类型(对象、结构体等)。二者都属于抽象数据类型。下面给出描述抽象数据类型的标准格式。

    ADT 抽象数据类型名
    Data
        数据元素之间逻辑关系的定义
    Operation
        操作1
            初始条件
            操作结果描述
        操作2
            ……
        操作n
            ……
    endAD

     二、什么是算法

      算法是解决特定问题求解步骤的描述。在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。简单说算法也就是解决问题的方法

      一个好的算法要满足

      1,正确性

        没有语法错误,对于合法的输入能产生满足要求的输出;对于非法的输入能够得出满足规格说明的结果;对于刁难的测试数据也有满足要求的输出

      2,可读性

        算法设计的目的之一是便于阅读、理解和交流。

      3,健壮性 

         一个好的算法应该能对输入数据不合法的情况做合适的处理,而不是产生异常或者莫名其妙的结果。

      4,时间效率高和存储量低

        时间和空间复杂度分析。事后统计方法有很大缺陷,不科学,不准确,我们一般使用事前分析估算方法来评判算法的效率。在计算机程序编制前,依据统计方法对算法进行估算

        我们在分析一个算法的运行时间时,重要的是把基本操作的数量与输入规模关联起来,即基本操作的数量必须表示成输入规模的函数。随着n值越来越大,它们在时间效率上的差异也就越来越大。

    那么我们如何来表示算法在时间上的差异呢?这就引入了算法时间复杂度

     三、时间复杂度

      判断一个算法好不好,我们通过少量的数据是不能做出准确判断的,我们可以对比算法的关键执行次数函数的渐进增长性,得到某个算法随着n的增大,越来越优于(或劣于)另一算法。

      在判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应该关注主项(最高阶项)的阶数分析高阶项,关键就是分析循环结构的运行情况

      算法的时间复杂度,也就是算法的时间量度,记作:T(n) = O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的时间复杂度。其中f(n)是问题规模n的某个函数。

      1,常数阶:对于单纯的分支结构(不包含在循环结构里),执行次数是恒定的,不会随着n的变大而发生变化,其时间复杂度是O(1).

       2,线性阶:对于下面这段代码,因为循环体中的代码要执行n次,所以它的时间复杂度为O(n)

    int i;
    for(i = 0; i < n; i++)

      3,对数阶:看下面这段代码。

    int count = 1;
    while (count < n){
        count  = count * 2;
    }

      count每次循环之后乘2,距离n更接近了一倍。也就是说,有多少个2相乘后大于n,则会退出循环。由2x=n得到x = log2n。所以这个循环的时间复杂度是O(logn)

      4,平方阶:对于常规的循环嵌套

    for(i = 0; i < n; i++){
        for(j = 0; j < n; j++){
            //一段O(1)的程序
        }
    }    

    这段代码的时间复杂度为O(n2);如果外循环的循环次数改为m。时间复杂度就变为O(m×n)。

    我们可以总结得出:循环的时间复杂度等于循环体的复杂度乘以该循环运行的次数

    看下面这个循环嵌套。

    for (i = 0; i < n; i++){
        for (j = i; j < n; j++){
        //一段O(1)的程序 } }

    当i = 0时,内循环执行n次;当i = 1时,内循环执行了n - 1次;…… ; 当i= n -1 时,内循环执行了一次。所以总执行次数为

      n + (n - 1 )+ (n - 2) + …… + 1 = n(n + 1)/2 = n2/2 + n/2 。去除不予考虑的常熟、低阶项等,得到时间复杂度为O(n2).

    常用的时间复杂度所耗费的时间从小到大依次是:

      O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)

      O(nlogn)会在后面讨论到,待补充。  O(n3) 及更高的时间复杂度太高,一般不讨论。

       在实际情况中,遇到的问题都会有好有坏。比如在数组中顺序查找一个数字,最好的情况一开始找第一个位置就是目标;最坏的情况找到最后一个位置才发现目标。那么对应的最好情况的时间复杂度就是O(1),最坏情况就是O(n)。除非特别指定,我们提到的时间复杂度都是按照最坏情况来考虑的运行时间。那么我们在设计算法的时候希望的是哪个运行时间呢?我们希望的是平均运行时间,对于上面的例子就是n/2.平均运行时间是所有情况中最有意义的,因为它是期望的运行时间。

     四、空间复杂度

      我们在写代码时,完全可以用空间换取时间。 举个例子,要判断某年是否是闰年:常规方法时写一个算法,设置判断条件,通过计算得到是否是闰年。而另一个方法是,设置一个足够长的,比如2050个元素的数组,把所有年份按下标对应,如果是闰年,数组值设为1,如果不是值设为0.这样判断是否是闰年可以直接到数组中按照下标取值。这样我们的运行时间将为了O(1) ,但是硬盘或者内存要存储这样一个很长的数组。这就是空间换取时间的做法。

      到底哪个好?看你要用在什么地方。

      算法的空间复杂度通过计算算法所需的存储空间实现,算法弓箭复杂度的计算公式记作:S(n) = O(f(n))。 n为问题规模,f(n)为语句关于n所占存储空间的函数。

  • 相关阅读:
    关于HTML面试题汇总之H5
    HTML5的页面资源预加载技术(Link prefetch)加速页面加载
    linux下搭建SVN服务器完全手册
    HTML5标签学习
    22个HTML5的初级技巧
    h5 audio播放音频文件
    html5适应屏幕的方案
    富文本编辑器的使用
    Array.prototype.filter()
    安装谷歌助手教程
  • 原文地址:https://www.cnblogs.com/Joey777210/p/11925883.html
Copyright © 2020-2023  润新知