目录
知识结构
比较基础的入门知识点,时间复杂度和空间复杂度的计算,算法的设计是主要的考察点。
数据结构的基本概念
基本概念和术语
1 数据
数据是信息的载体,是对客观事物的符号表示。在计算机科学中指所有能输入到计算机中并且被结算及程序处理的符号的总称。如整数、实数和字符串。
2 数据元素
数据元素是数据的基本单位,通常作为一个整体进行考虑和处理。
3 数据项
一个数据元素可由若干个数据项组成。数据项是构成数据元素的不可分割的最小单位。
4 数据对象
数据对象是具有相同性质的数据元素的集合,是数据的一个子集。如大写字母就是一个数据对象。
5 数据类型
数据类型是一组性质相同的值的集合以及定义在这个值的集合上的一组操作的总称。
① 原子类型:其值不可再分。
② 结构类型:其值可以再分解为若干成分(分量)。
③ 抽象数据类型:抽象数据组织和与之相关的操作。
6 抽象数据类型
抽象数据类型(ADT)是指一个数学模型以及定义在此数学模型上的一组操作。抽象数据类型需要通过固有数据类型(高级编程语言中已实现的数据类型)来实现。抽象数据类型的定义仅取决于它的一组逻辑特性,而与其在计算机内部如何表示和实现无关。不论其内部结构如何变化,只要它的数学特性不变,都不影响其外部的使用。通常用(数据对象、数据关系、基本操作集)这样的三元组来表示抽象数据类型,因而可以构成一个完整的数据结构定义。
对一个抽象数据类型进行定义时,必须给出它的名字及各运算的运算符名,即函数名,并且规定这些函数的参数性质。一旦定义了一个抽象数据类型及具体实现,程序设计中就可以像使用基本数据类型那样,十分方便地使用抽象数据类型。
7 数据结构
在任何问题中,数据元素都不是孤立存在的,而是在它们之间存在着某种关系,这种数据元素相互之间的关系称为结构(Structure)。数据结构是相互之间存在一种或多种特定关系的数据元素的集合。数据结构包括三方面的内容:逻辑结构、存储结构和数据的运算。数据的逻辑结构和存储结构是密不可分的两个方面,一个算法的设计取决于所选定的逻辑结构,而算法的实现依赖于所采用的存储结构。
数据结构三要素
1 数据的逻辑结构
逻辑结构是指数据元素之间的逻辑关系。它与数据的存储结构无关,同一种逻辑结构可以有多种存储结构。线性表是典型的线性结构;集合、树和图是典型的非线性结构。数据的逻辑结构分类见图。
(1)线性结构
线性结构是一个数据元素的有序集合,有3个基本特征:
① 存在唯一的“第一个元素”和“最后一个元素”。
② 除1)中元素外,所有元素都有唯一的前驱和后继。
③ 结构中的元素存在着“一对一”的关系。如 (a1, a2, ... , an),a1 就为第一个元素,an 就为最后一个元素。
(2)非线性结构
1)树形结构
数据元素(在树中称为结点)按分支关系组织起来的结构,很象自然界中的树那样。结构中的元素存在着“一对多”的关系。
2)图状结构
“多对多”关系形成的逻辑结构。其中每个元素的直接前趋和直接后继数目都不限。
2 数据的存储(物理)结构
数据的存储结构是其逻辑结构在计算中的表示(映像),也可以理解为数据的存储结构是用计算机语言实现的逻辑结构,它依赖于计算机语言数据的逻辑结构。它包括数据元素的表示和关系的表示。当数据元素是由若干项数据构成的时候,数据项的表示称为数据域。
数据元素之间的关系在计算机中有两种不同的表示方法:顺序映像和非顺序映像(从逻辑关系上分类)。对应两种不同的存储结构:顺序存储结构和链式存储结构。顺序映像借助数据元素在内存中的相对位置来表示它们之间的逻辑关系;非顺序映像借助的则是指针。
注意区分数据结构和其要素。如顺序表、哈希表和单链表,它们既描述逻辑结构,又描述存储结构和数据运算。所以不能简单的将它们归为逻辑结构或者存储结构,而是一种数据结构。
数据结构的四种存储结构:
1)顺序存储
把逻辑上相邻的元素存储在物理位置上相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。优点是可以实现随机存取,每个元素占用最少的存储空间;缺点是只能使用相邻的一整块存储单元,因此可能产生较多的外部碎片。
2)链式存储
不要求逻辑上相邻的元素在物理位置上也相邻,借助指示元素存储地址的指针表示元素之间的逻辑关系。优点是不会出现碎片现象,充分利用所有存储单元;缺点是每个元素因存储指针而占用额外的存储空间,并且只能实现顺序存取。值得注意的是,链式存储的结点的存储空间可以不连续,但是结点内的存储单元地址必须是连续的。
3)索引存储
在存储元素信息的同时,还建立附加的索引表来标致数据元素的地址。索引表中的每一项称为索引项,索引项的一般形式是:<关键字,地址>,地址作为指向数据元素的指针。优点是检索速度快;缺点是增加了附加的索引表,会占用较多的存储空间。另外,在增加和删除数据时要修改索引表,因而会花费较多的时间。
4)散列存储:由数据元素的关键字通过散列函数直接计算出该元素的存储地址,又称为 Hash 存储,本质上是顺序存储方法的扩展。其优点是检索、增加和删除结点的操作都很快;缺点是如果散列函数不好可能出现元素存储单元的冲突,而解决冲突会增加时间和空间开销。
哈希表(hash table)是实现字典操作的一种有效的数据结构。
尽管最坏的情况下,散列表中查找一个元素的时间与链表中查找的时间相同,达到了O(n)。然而实际应用中,散列的查找的性能是极好的。在一些合理的假设下,在散列表中查找一个元素的平均时间是O(1)。
3 数据的运算
施加在数据上的运算包括运算的定义和实现。运算的定义是针对逻辑结构的,指出运算的功能;运算的实现是针对存储结构的,指出运算的具体操作步骤。
算法的基本概念
基本概念
1 算法(Algorithm)
算法是对特定问题求解步骤的一种描述,它是指令的有限序列,其中每一条指令表示一个或多个操作。可以理解为由基本运算及规定的运算顺序所构成的完整的解题步骤
2 算法的特性
1)有穷性
一个算法必须保证执行有限步之后结束,每一步都在有限时间内完成。
2)确定性
算法中每一条指令必须有确切的含义,读者理解时不会产生二义性。即对于相同的输入只能得出相同的输出。
3)可行性
算法中描述的操作都必须可以通过已经实现的基本运算执行有限次来实现。
4)输入
一个算法有0个或多个的输入,以刻画运算对象的初始情况。这些输入取自于某个特定的对象的集合,所谓0个输入是指算法本身确定了初始条件。
5)输出
一个算法有一个或多个的输出,以反映对输入数据加工后的结果。这些输出是同输入有着某种特定关系的量,没有输出的算法时毫无意义的。
3 算法的设计目标
1)正确性
最重要且最基本的标准,算法应当能够正确地解决求解问题。
2)可读性
算法应当具有良好的可读性。
3)健壮性
算法应该有很好的容错性,能够对非法数据做出反应和进行处理。
4)高效率与低存储量需求
算法的效率主要指算法执行的时间,存储量需求是指算法执行过程中所需要的最大存储空间,这两者都与问题的规模有关。
同一个算法,实现语言的级别越高,其执行效率越低。
算法效率的度量
1 时间复杂度
通常将算法中基本操作的执行次数(时间)作为算法时间复杂度的度量。一个语句的频度是指该语句在算法中被重复执行的次数。算法中所有语句的频度之和记作 T(n),它是该算法问题规模 n 的函数,时间复杂度主要分析 T(n) 的数量级。算法中的基本运算(最深层循环内的语句)的频度与 T(n) 同数量级,所以通常采用算法中基本运算的频度 f(n) 来分析算法的时间复杂度。因此,算法的时间复杂度记为:T(n) = O(f(n))。
上式中“O”的含义是 T(n) 的数量级,其严格的数学定义是:若 T(n) 和 f(n) 是定义在正整数集合上的两个函数,则存在正常数 C 和 n0,使得当 n >= n0 时,都满足 0 <= T(n) <= Cf(n)。
算法的时间复杂度不仅依赖于问题的规模 n,也取决于待输入数据的性质(如输入数据元素的初始状态)。
例如在数组 A[0 … n - 1] 中,查找给定值 k 的算法如下:
i = n - 1; while (i >= 0 && (A[i] != k)) i--; return i;
此算法中的语句3(基本运算)的频度不仅与问题规模n有关,还与 A 中的各元素取值及 k 的取值有关:
① 若 A 中没有与 k 相等的元素,则语句3的频度 f(n) = n。
② 若 A 的最后一个元素等于 k,则语句3的频度 f(n) = 0。
最坏时间复杂度是指在最坏情况下,算法的时间复杂度。
平均时间复杂度是指所有可能输入实例在等概率出现的情况下,算法的期望运行时间。
最好时间复杂度是指在最好情况下,算法的时间复杂度。
一般总是考虑在最坏情况下的时间复杂度,以保证算法的运行时间不会比它更长。
在分析一个程序的时间复杂性时,有以下两条规则:
1)加法规则
2)乘法规则
常用的时间复杂度比较关系
算法的时间复杂度是由基本运算的频度而来,因此在分析算法的时间效率时,不要给时间复杂度随便赋值,应当严格遵循上述公式。
2 空间复杂度
算法的空间复杂度 S(n) 定义为该算法所耗费的存储空间,它是问题规模 n 的函数。渐近空间复杂度也常简称为空间复杂度,记作 S(n) = O(g(n))。
算法原地工作:若算法执行时所需要的辅助空间相对于输入数据量而言是一个常数,则称这个算法为原地工作,辅助空间为 O(1)。
3 计算算法复杂的的基本步骤
1)确定算法中的基本操作以及问题的规模。
2)根据基本操作执行情况计算出规模 n 的函数 f(n),并确定时间复杂度为 T(n) = O(f(n)) 中增长最快的项。注意区分执行次数和时间复杂度的概念,执行次数只是时间复杂度的一个度量,并不直接等于时间复杂度。一般需要通过求和计算来求得执行次数。