数据结构与算法——第一章——绪论

1.2数据结构的概念

1.2.1基本概念和术语

1.数据

  • 是信息的载体,是所有能够被计算机识别、存储和加工处理的符号的总称。

  • 是计算机程序加工的原料。

  • 可以是数值数据(整数、实数、复数),也可以是非数值数据(字符、文字、图形、图像、语音)。

2.数据项

  • 是具有独立含义的标识单位

  • 是数据不可分割的最小单位

  • 数据项有名和值之分:数据项名是一个数据项的标志,用变量定义;而数据项值是它的一个可能取值

  • 数据项具有一定的类型,依数据项的取值类型而定

3.数据元素

  • 数据元素是数据的基本单位

  • 在不同的条件下,数据数据元素又可称为元素、结点、顶点、、记录等

  • 有时,一个数据元素可由若干个数据项组成(初等项:不能再分割的最小单位;组合项:可以再划分)

4.数据对象

  • 数据对象或数据元素类是具有相同性质的数据元素的集合

  • 数据元素都具有相同的性质(元素值不一定相等),属于同一数据对象(数据元素类),数据元素是数据元素类的实例

5.数据结构

数据结构是指相互之间存在着一种或多种关系的数据元素的集合。数据结构涉及数据元素之间的逻辑关系,数据在计算机中的存储的方式和这些数据上定义的一组运算,一般称这3个方面为数据的逻辑结构、数据的存储结构和数据的运算。

(1)数据的逻辑结构

数据元素之间不会孤立,在它们之间存在着逻辑关系,这种数据元素之间的关系称为逻辑结构

  • 数据的逻辑结构包含两个要素:1.数据元素的集合;2.关系的集合

  • 在形式上,数据的逻辑结构通常可以采用一个二元组来表示:Data_Structure=(D,R);

    ​ 其中,D是数据元素的有限集,R是D上关系的有限集


    • 根据数据元素之间关系的不同性,数据的逻辑结构通常分为一下4类

      1)集合结构:在集合中,数据元素的关系是“属于同一集合”。集合是数据元素极为松散的一种结构。

      2)线性结构:数据元素关系“一对一”

      3)树形结构:数据元素关系“一对多”

      4)图形结构:数据元素关系“多对多”,又称网状结构

(2)数据的存储结构

数据的存储结构最常用的是:【1】顺序存储结构;【2】链式存储结构

1)顺序存储:把逻辑上相邻的元素存储在物理位置相邻的存储单元,结点间的逻辑关系由存储单元的邻接关系来体现

2)链式存储:对逻辑上相邻的元素不要求其物理位置相邻,元素间的逻辑关系通过附设的指针字段来表示

有时为了查找方便采用索引存储和散列存储

3)索引存储:在存储节点信息的同时,还建立附加的索引表。

​ 索引表的每一项包含关键字地址

​ 【1】关键字是能够唯一标识一个数据元素的数据项,【2】地址是指出数据元素所在的存储位置

​ 索引存储主要针对数据内容的存储,而不强调关系的存储,主要面向查找操作

4)散列存储:以数据元素关键字的值为自变量,通过某个函数(散列函数)计算出该元素的存储位置。索引存储也是针对数据内容的存储方式。

一种逻辑结构可以有不同的存储方法

(3)数据的运算

运算是对数据的处理。运算与逻辑结构紧密相连,每种逻辑结构都有一个运算的集合。

1)引用型运算:不改变数据结构中原有的数据元素的状态,只根据需要读取某些信息

2)加工型运算:运算的结果会改变数据结构中原有的数据的状态,如:数据元素的内容、个数等

数据的运算是定义在数据的逻辑结构上的,但运算的具体实现实在数据的存储结构上进行的。在数据的逻辑结构和存储结构给定之后,如果定义的运算集及运算性质不同,也会导致完全不同的数据结构

1.2.2抽象数据类型

1.数据类型

  1. 定义

  • 数据类型是和数据结构密切相关的一个概念。

  • 用以描述程序中操作对象的特性

  • 在高级语言编写的程序中,每个变量、常量、或表达式都有一个它所属的确定的数据类型

  • 类型显示地或隐含地规定了在程序执行期间变量或表达式所有可能的取值范围,以及它在这些值上允许进行的操作。因此,数据类型是一个值得集合定义在这个值集上的一组操作的总称

​ 2.类型

  • 原子类型:原子类型的值是不可分解的。如C语言中整型、字符型、浮点型、双精度型等基本类型,分别保留字int,char,float,double标识。

  • 结构类型:结构类型的值是由若干成分按某种结构组成的,因此是可分解的,并且它的成分可以是非结构的,也可以是结构的。例如,数组的值由若干分量组成,每个分量可以是整数,也可以是数组等,

    • 在某种意义上,数据结构可以看成是“一组具有相同结构的值”,而数据类型则可以看成是由一种数据结构和定义在其上的一组操作所组成的。

2.抽象数据类型

  • 抽象数据类型是指一个数学模型以及定义在该模型上的一组操作。

  • 抽象数据类型的定义取决于它的一组逻辑特性,与它在计算机内部如何表示和实现无关。即:不论其内部结构如何变化,只要他的数学特性不变,都不影响其外部的使用。

  • 抽象数据类型和数据类型实质上是一个概念。“抽象”的意义在于数据类型的数据抽象特性

  • 抽象数据类型的范畴更广,它不局限于前述各处理器中已定义并实现的数据类型,还包括用户在设计软件系统时自己定义的数据类型。

  • 为提高软件的重用性,要求在构成软件系统的每个相对独立的模块上,定义一组数据和施于这些数据上的一组操作,并在模块的内部给出这些数据的表示及其操作细节,而在模块的外部使用的只是抽象的数据及抽象的操作。

  • 抽象数据类型的定义可以由一种数据结构和定义在其上的一组操作组成,而数据结构又包括数据元素及元素间的关系。因此抽象数据类型一般可以由元素、关系及操作3种要素来定义。

  • 抽象数据类型的特征是使用与实现相分离,实行封装和信息隐蔽。就是说,在抽象数据类型设计时,把类型的定义与实现分离开来。

1.3算法

算法设计时要先确定相应的数据结构,在讨论某一种数据结构时必然会涉及相应的算法

1.3.1算法及其特征

算法是对特定问题 求解的一种描述,是指令的有限数列。其中每一条指令表示一个或多个操作。

  1. 算法特性

    1)有穷性:必须在有穷步结束,即在有限时间内完成

    2)确定性:每一步有确切的定义,无二义性。算法的执行对应着相同的输入仅有唯一的一条路径,即相同输入必然有相同输出

    3)可行性:算法中的每一步都可以通过已经实现的基本运算的有限次执行得以实现

    4)输入:一个算法有零个或多个输入这些输入取自特定的数据对象集合

    5)输出:一个算法具有一个或多个输出,这些输出与输入之间存在某种特定的关系

  2. 算法与程序

    1. 算法含义与程序十分相似,但又有区别

    2. 程序不一定满足有穷性。例如操作系统只要不别破坏就会永不停止,持续处于动态。因此操作系统不是一个算法

    3. 程序中的指令必须是机器可执行的,& 而算法指令则无此限制。

    4. 算法代表了对问题的解,而程序则是算法在计算机上的特定的实现。

    5. 一个算法若用程序设计语言来描述,那么它就是一个程序

  3. 算法与数据结构

    算法与数据结构相辅相成,解决某一特定类型问题的算法可以选定不同的数据结构,而且选择恰当与否直接影响算法的效率。反之,一种数据结构的优劣由各种算法的执行来体现

  4. 算法要求

    1)正确:算法的执行结果应当满足预定的功能和性能要求

    2)可读:不仅让机器来执行,更重要的是便于人的阅读和理解

    3)健壮:对于不合法的输入能作适当处理,避免莫名输出或其他严重后果

    4)高效:有较好的时空性能

1.3.2算法的描述

  1. 描述方法

    1)自然语言:优点:简单易于理解;缺点:不够严谨

    2)程序流程图、N-S图:优点:简洁明了;缺点:不能直接在计算机上执行

    3)*某种程序设计语言:不容易、不直观,借助注释才能被人理解

    4)伪码语言:易于描述理解,解决了理解与执行这两者之间的矛盾

  2. 伪码语言

    伪码语言介于高级程序设计语言与自然语言之间,既忽略了高级语言中严格的语法规则与描述细节,又比自然语言更接近程序设计语言,虽然不能直接执行但容易转换成高级语言

1.3.3算法的性能分析

从算法的计算时间与所需存储空间来评价一个算法的优劣

  1. 时间复杂度T(n)

    1. 限制时间的因素

      1)硬件的速度:主要指机器的指令性能和速度。例:64位机比32位机快;主频2GHz的机器一般比1GHz的机器快

      2)书写程序的语言:实现语言的级别越低,其执行效率就越高

      3)编译程序所生成目标代码的质量:代码优化较好的编译程序所生成的目标代码的质量较高

      4)问题的规模:

    2. 如何比较算法的执行时间

      将上述与计算机相关的软、硬件因素都确定下来,这样一个算法的运行工作量的大小就只依赖于问题的规模(通常用正整数n表示),或者说他是问题规模的函数

    3. 时间复杂度计算

      一个算法执行所耗费的时间,是算法中中所有语句执行时间之和,

      每条语句执行时间=该语句执行一次所需时间*语句重复次数

      其中一个语句重复执行的次数称为语句的频度

      算法时间复杂度T(n)表示:T(n)= ∑(tixc)

      ​                                          语句i

      数据结构与算法——第一章——绪论_第1张图片其中ti表示语句i执行一次的时间,ci表示语句i的频度

       

    4. 例【1-4】下面的程序段永来求两个n阶方阵A和B的乘积C

      for(i=0;i 

      右边列出了各语句的频度,因而算法的时间复杂度T(n)为:

      T(n)=(n+1)+n(n+1)+n^2+n^2(n+1)+n^3=2n^3+3n^2+2n+1

      可见T(n)是矩阵阶数n的函数

      当问题规模很大时,T(n)表达式中有些项占主导地位,其他项可以忽略不计。例如在【例1-4】中,当n很大时,T(n)中起主导作用的最高次项2n^3,显然:

    数据结构与算法——第一章——绪论_第2张图片T(n)与n^3是同数量级的,T(n)可近似用n^3来表示

     

    1. 定义(大O记号):设T(n)是问题规模n的函数f(n),存在两个正常数c和n0,使对所有的n(n>=n0),

      有:T(n)<=cf(n),记为T(n)=Of(n)

      例:T(n)=20n^3+25n^2+9,则T(n)=O(n^3)

      O记号表示算法的渐进时间复杂度,简称时间复杂度

    2. 常见的时间复杂度按数量级递增排列:(O(1)表示常数计算时间)

      O(1)

    3. 一个算法是由控制结构和基本语句构成,执行时间取决于二者综合

    4. 比较同一问题不同算法

      从算法中选取一种对于所研究问题来说是基本运算的语句,以该基本语句重复执行的次数作为算法的时间度量。基本远算一般应选取频度最大的语句,如:深层循环体内的语句。

    5. 有些算法中时间复杂度不仅与问题规模有关,还与输入数据集的状态有关。这类问题从概率的角度出发讨论。也可根据数据集可能的最好最坏情况来估算最好或最坏时间复杂度。也可以对数据集做某种假定情况下,讨论算法的平均时间复杂度。

      冒泡排序

      void BubleSort(int a[],int n)
      {
        for(i=0;ia[j+1])
        {
          a[j]<=>a[j+1];
          swap=1;
        }
        }
      }

      选择交换相邻的元素"a[j]<=>a[j+1];"作为基本操作。当a中序列自小到大有序时,基本操作的执行次数是0;当a中序列自大到小有序时,基本操作的执行次数是n(n+1)/2。而n个元素组成的输入集可能有n!种排列情况,若各种情况等概率出现,则冒泡排序的平均时间复杂度T(n)=O(n^2)

      很多情况下,输入数据集的分布概率难以确定,常用方法是讨论最坏复杂度,即分析算法执行时间的上限

  2. 空间复杂度

    1. 定义:一个算法的空间复杂度是指算法运行从开始到结束所需的存储量。算法的一次运行是针对所求解的问题的某一特定实例而言的。

    2. 算法所需的存储空间包括以下两个部分:

      1)固定部分:这部分空间与所处理数据的大小和个数无关,主要包括程序代码、常量、简单变量,定长成分的结构变量所占的空间。

      2)可变部分:这部分空间大小与算法在某次执行过程中处理的特定数据大小和规模有关。例:100个数据元素算法与1000个不同

    3. 当问题规模大时,可变部分可能会远大于固定部分,所以一般只讨论算法的渐进空间复杂度。算法的空间复杂度分析与时间复杂度类似。

1.4递归

递归使程序设计和算法描述形式简洁且易于理解

1.4.1递归的概念

  1. 递归是一个过程或函数直接或间接调用自身的一种方法,它可以把一个大型的问题层层转换为一个与原问题相似、但规模较小的问题来求解。

  2. 递归实质上是一种循环结构,它把“较复杂”情况的计算归结为“较简易”情况的计算,一直归结到“最简单”情况的计算为止。

  3. 直接或间接调用自身的程序称为递归程序。

  4. 递归是一种特殊的嵌套调用,是某个函数调用自己,而不是调用另外一个函数。是一种方法(函数)直接或间接调用自身的编程技术。

  5. 递归概念上简化问题,但不提高效率

1.4.2递归调用的实现原理

  1. 递归算法的构成

    递归是程序设计中的数学归纳法

    (1)递归算法的条件

    1)需要解决的问题可以化为一个或多个子问题来求解,而这些子问题的求解方法与原来的问题完全相同,只是数量规模上不相同。

    2)递归调用的次数必须有限

    3)**必须有结束递归的条件(边界条件)来终止递归。

    (2)递归算法的设计步骤

    1)将规模较大的问题分解为一个或多个规模较小的而又类似于原问题特性的子问题,即将较大的问题递归地用较小的子问题来描述,解原问题的方法同样可用来解决子问题。【递归的步骤】

    2)是确定一个或多个不需要分解、可直接求解的最小问题。【最小问题是递归的终结条件】

    (3)1)递归函数的调用类似于多层函数的嵌套调用,只是调用单位和被调用单位是同一个函数。

    ​ 2)递归的方法只需少量的程序代码就可描述出解题过程所需的重复计算,大大的减少了程序的代码量。

  2. 递归调用的内部过程

    1. 递归过程分为两个阶段

      (1)1)递归过程:将原始问题不断转化为规模小一级的新问题

      ​ 2)回溯过程:从已知条件出发,沿递归的逆过程,逐一求值返回,直至递归的初始处,完成递归调 用。

      (2) 在这两个阶段中,系统会分别完成一系列操作。在第归调用之前,系统需完成以下三件事:

      1)为被调用的局部变量分配存储区。

      2)将所有的实参、返回地址等信息传递给被调用过程保存。

      3)将控制转移到被调用过程的入口。

      (3)从被调用过程返回调用过程之前,系统也应完成以下3件工作:

      1)保存被调用过程的计算结果。

      2)释放被调用过程的数据区。

      3)依照被调用过程保存的返回地址将控制转移到调用过程。

      在计算机中是通过使用系统栈来完成上述操作的。

    2. 递归算法解决问题的方式和特点

      将初始问题可转化为解决方法相同的新问题,而新问题的规模要比原始问题小,新问题又可转化为规模更小的问题……直至最终归结到最基本的情况(可以简单解决的问题)——递归的终结条件。

    3. 递归算法的缺点

      会占用大量的内存,消耗的大量的时间,造成执行效率低。

    4. 递归算法的优点

      程序简洁、清晰、可读性好。

1.4.3递归转换为非递归

  1. 递归转换为递推

    1. 当地硅酸发所涉及的数据定义形式是递归的情况下,通常可以将递归算法转化为递推算法,用递归的边界条件作为递推的边界条件。比如:求阶乘、斐波那契数列等,

    2. 递推也是一种从已知条件出发,用一种具体的算法,一步一步接近未知,一般采用循环结构,经常和枚举配合使用。递推算法在求解的过程中,每一个中间量都是已知,而且没有重复计算,运算简洁,但是书写代码和理解代码比较难。

    3. 递归与递推的比较

      递归是从未知到已知,再从已知返回未知,利用子问题与父问题的关系,进而构造成有递归性的函数。而递推是从已知到未知,类似于一般数学解题的思路,从未知与已知的顺序上来看,他们好像是互逆过程,其实不然。递归把问题简化,抓的是问题与子问题的联系,而递推是把中间解推进,抓的是中间量与更靠近未知的中间量的联系,两者不同,不能简单看做互逆过程。【?】

  2. 递归转化为回溯

    1. 回溯方法的步骤:

      1)定义一个解空间,它包含问题的解。

      2)用适于搜索的方式组织该空间。

      3)用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。

    2. 回溯算法的一个有趣特性是在搜索执行的同时产生解空间。在搜索期间的任何时刻,仅保留从开始结点到当前结点的路径。因此,回溯算法的空间需求为O(从开始结点起最长路径的长度)。这个特性非常重要,因为解空间的大小通常是最长路径的长度的指数或阶乘。所以要存储全部解空间的话,再多的空间也不够用。【?】

你可能感兴趣的:(数据结构)