通常,用计算机解决一个问题的步骤:
具体问题抽象为数学模型
实质:
设计算法
编程、调试、运行
早期,计算机主要用于数值计算,其特点为:数据元素间的关系简单,计算复杂
随着计算机应用领域的拓展,计算机被越来越多的用于非数值计算
描述非数值计算问题的数学模型不是数学方程,而是诸如表、树和图之类的具有逻辑关系的数据
数据结构是一门研究非数值计算的程序设计中计算机的操作对象以及他们之间的关系和操作的学科
数据
数据元素
数据项
构成数据元素的不可分割的最小单位
数据、数据元素、数据项三者之间的关系:
数据 > 数据元素 > 数据项
数据对象
数据结构
数据结构包括以下三个方面的内容:
数据结构的两个层次
逻辑结构的种类
划分方法一
线性结构
有且仅有一个开始和一个终端结点,并且所有结点都最多只有一个直接前趋和一个直接后继。
例如:线性表、栈、队列、串
非线性结构
一个结点可能有多个直接前趋和直接后继
例如:树、图
划分方式二——四类基本逻辑结构
存储结构的种类
四种基本的存储结构:
顺序存储结构
用一组连续的存储单元依次存储数据元素,数据元素之间的逻辑关系由元素的存储位置来表示。
C语言中用数组来实现顺序存储结构
链式存储结构
用一组任意的存储单元存储数据元素,数据元素之间的逻辑关系用指针来表示。
C语言中用指针来实现链式存储结构
索引存储结构
在存储结点信息的同时,还建立附加的索引表
散列存储结构
根据结点的关键字直接计算出该结点的存储地址
数据类型和抽象数据类型
一些最基本数据结构可以用数据类型来实现,如数组、字符串等;
而另一些常用的数据结构,如栈、队列、树、图等,不能直接用数据类型来表示。
数据类型的作用:
数据类型:数据类型是一组性质相同的值的集合以及定义于这个值集合上的一组操作的总称。
数据类型=值的集合+值集合上的一组操作
抽象数据类型(ADT):是指一个数学模型以及定义在此数学模型上的一组操作
由用户定义,从问题抽象出数据模型(逻辑结构)
还包括定义在数据模型上的一组抽象运算(相关操作)
不考虑计算机内的具体存储结构与运算的具体实现算法
抽象数据类型的形式定义
抽象数据类型可用(D,S,P)三元组表示。其中:D是数据对象、S是D上的关系集、P是对D的基本操作集。
一个抽象数据类型的定义格式如下:
ADT 抽象数据类型名{数据对象:<数据对象的定义>数据关系:<数据关系的定义>基本操作:<基本操作的定义>
} ADT 抽象数据类型名
其中:数据对象、数据关系的定义用伪代码描述;
基本操作的定义格式为:
基本操作名(参数表)
初始条件:<初始条件描述>
操作结果:<操作结果描述>
基本操作定义格式说明:
参数表:赋值参数 只为操作提供输入值。
引用参数 以&打头,除可提供输入值外,还将返回操作结果。
初始条件:描述操作执行之前数据结构和参数应满足的条件,若不满足,则操作失败,并返回相应出错信息。若初始条 件为空,则省略之。
操作结果:说明操作正常完成之后,数据结构的变化状况和应返回的结果。
抽象数据类型如何实现
抽象数据类型可以通过固有的数据类型(如整型、实型、字符型等)来表示和实现。即利用处理器中已存在的数据类型来说明新的结构,用已经实现的操作来组合新的操作。
用C语言真正实现抽象数据类型的定义
例如:抽象数据类型“复数”的实现
typedef struct{float realpart; /* 实部 */float imagpart; /* 虚部 */
}Complex /* 定义复数抽象类型 */void assign(Complex *A,float real, float imag); /* 赋值 */
void add(Complex *A,float real, float imag); /* A+B */
...
算法
算法的定义
对特定问题求解方法和步骤的一种描述,它是指令的有限序列。其中每个指令表示一个或多个操作。
简而言之,算法就是解决问题的方法和步骤
算法的描述
算法与程序
**算法特性:一个算法必须具备以下五个重要特性 **
算法设计的要求
正确性:算法满足问题要求,能正确解决问题
算法转化为程序后要注意:
通过以第三层意义上的正确性作为衡量一个算法是否合格的标准
可读性
健壮性
高效性
要求花费尽量少的时间和尽量低的存储需求
算法分析
算法分析的目的是看算法实际是否可行,并在同一问题存在多的算法时可进行性能上的比较,以便从中挑选出比较优的算法。
一个好的算法首先要具备正确性,然后是健壮性,可读性,在几个方面都满足的情况下,主要考虑算法的效率,通过算法的效率高低来评判不同算法的优劣程度。
算法效率以下两个方面来考虑:
时间效率和空间效率有时候是矛盾的。
算法时间效率的度量
事前分析方法:
一个算法的运行时间是指一个算法在计算机上运行所耗费的时间大致可以等于计算机执行一种简单的操作(如赋值、比较、移动等)所需的时间与算法中进行的简单操作**次数乘积 **。
算法运行时间=一个简单操作所需的时间×简单操作次数
也即算法中每条语句的执行时间之和
算法运行时间=Σ每条语句的执行次数×该语句执行一次所需的时间
算法运行时间=Σ每条语句频度×该语句执行一次所需的时间
算法时间复杂度的渐进表示法
分析算法时间复杂度的基本方法
定理1.1
若f(n)=amnm+am−1nm−1+...+a1n+a0是m次多项式,则T(n)=O(nm)若f(n)=a_mn^m+a_{m-1}n^{m-1}+...+a_1n+a_0是m次多项式,则T(n)=O(n^m) 若f(n)=amnm+am−1nm−1+...+a1n+a0是m次多项式,则T(n)=O(nm)
时间复杂度是由嵌套最深层语句的频度决定的
算法时间复杂度
请注意:有的情况下,算法中基本操作重复执行的次数还随问题的输入数据集不同而不同
一般总是考虑在最坏情况下的时间复杂度,以保证算法的运行时间不会比它更长。
对于复杂的算法,可以将它分成几个容易估算的部分,然后利用大O加法法则和乘法法则,计算算法的时间复杂度:
加法规则
T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n)))T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n))) T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n)))
乘法规则
T(n)=T1(n)×T2(n)=O(f(n))×O(g(n))=O(f(n)×g(n))T(n)=T1(n)×T2(n)=O(f(n))×O(g(n))=O(f(n)×g(n)) T(n)=T1(n)×T2(n)=O(f(n))×O(g(n))=O(f(n)×g(n))
当n取得很大时,指数时间算法和多项式时间算法在所需时间上非常悬殊
时间复杂度T(n)按数量级递增顺序为:
常数阶 | 对数阶 | 线性阶 | 线性对数阶 | 平方阶 | 立方阶 | … | k次方阶 | 指数阶 |
---|---|---|---|---|---|---|---|---|
O(1) | O(log_2 n) | O(n) | O(n log_2 n) | O(n^2) | O(n^3) | O(n^k) | O(2^n) |
渐进空间复杂度
空间复杂度:算法所需存储空间的度量,记作:S(n)=O(f(n)),其中n为问题的规模(或大小)
算法要占据的空间