目录
1.1 数据结构的研究内容
计算机既可以用于数值计算,也能用于非数值计算,而非数值计算主要包括处理字符、表格和图像等具有一定结构的数据。这些数据内容之间存在着某种关系,根据这种关系设计出高效的处理数据的算法则是数据结构的主要研究内容。
-
用计算机解决问题的步骤:
1、从问题中抽象出数学模型;
2.、提取操作对象;
3.、分析操作对象之间的关系;
4、 将操作对象之间的关系用数学语言进行描述——数据结构 -
数值计算
从实际问题中抽象出来的数学模型可以建立相应的数学方程进行处理 -
非数值计算——无法用数学方程建立数学模型
1、 学生信息管理系统——线性表:元素与元素之间是一对一的关系
2、文件系统的系统结构图——典型的树形结构:元素之间是一对多的关系
3、地图导航,求最短路径——网状结构 / 图:元素之间是多对多的关系
数据结构:是一门研究非数值计算程序设计中的操作对象,以及这些对象之间的关系和操作的学科
程序: 算法+数据结构
具体有关Markdown的使用查看链接:link
1.2 基本概念和术语
1.2.1 数据、数据元素、数据项和数据对象
- 数据(Data)
所有能输入到计算机并能被计算机处理的各种符号的总称。
- 常见的有关数据的描述:
- 信息的载体
- 是客观事物的符号表示
- 能被计算机识别、存储和加工
- 数据包含两类:
- 数值型数据:整数、实数等
- 非数值型数据:文字、图像、声音等
- 数据元素(Data Element)、数据项(Data Item)
数据元素:
是数据的基本组成单位,在计算机中通常作为一个整体进行考虑和处理,也被称为元素、结点、记录 等
数据项:
构成数据元素的、有独立含义的、不可分割的最小单位。例如,学生基本信息表中的学号、姓名、性别等都是数据项。
- 数据、数据元素、数据项三者之间的关系
数据 > 数据元素 > 数据项:即数据包含数据元素,数据元素包含数据项
- 数据对象
性质相同的数据元素的集合,是数据的一个子集
例如:
整数数据对象是集合N={0,±1,±2,···}
字母字符数据对象是集合C={‘A’, ‘B’, ··· , ‘Z’}
学生的基本信息表也可以是一个数据对象
1.2.2 数据结构:逻辑结构和存储结构
-
概念
①数据元素之间并不是孤立存在的,他们之间存在着某种关系,这种相互关系称为结构
②是指相互之间存在一种或多种特定关系的数据元素的集合包含:
- 数据元素之间的逻辑关系
- 数据元素及其关系在计算机内存中的表示(又称映射),称为数据的物理结构或存储结构
- 数据的运算与实现
-
逻辑结构
从逻辑关系描述数据,与存储无关,是独立于计算机的,因此,数据的逻辑结构可以看作是从具体问题抽象出来的数学模型。两个要素:数据元素、关系
-
划分方式1:
-
线性结构,例如:
线性表:一种典型的线性结构,例如学生信息表
栈和队列:具有特殊限制的线性表,数据操作只能在表的一端或两端进行
字符串:也是特殊的线性表,其特殊性表现在它的数据元素仅由一个字符组成
数组:线性表的推广,它的数据元素是一个线性表
广义表:线性表的推广,它的数据元素是一个线性表 -
非线性结构,如树、图等,也就是存在一对多、多对多的关系
树结构:包括树(具有多个分支的层次结构)和二叉树(具有两个分支的层次结构)
图结构:包含有向图(边是顶点的有序对)和无向图(边是顶点的无序对)
-
-
划分方式2:四类基本的逻辑结构
①集合结构:数据元素除了 “属于同一集合” 的关系外,别无其他的关系。
例如,确定一名学生是否为班级成员,只需将班级看作是一个集合结构。
②线性结构:数据元素之间存在一对一的关系
③树结构:数据元素之间存在一对多的关系
④图结构或网状结构:数据元素之间存在多对多的关系
注意:集合结构、树结构和图结构均属于非线性结构
四类基本逻辑结构关系图:
-
-
存储结构
也称物理结构,数据元素在计算机中有两种基本的存储结构,分别是顺序存储、链式存储。
还有一些其他的存储结构,例如:
索引存储结构:在存储结点信息同时建立附加的索引表,表中的项称为索引项,索引项的一般形式为:(关键字,地址)
散列存储结构:根据结点的关键字直接计算出该结点的存储地址-
顺序存储结构
①用一组连续的存储单元依次存储数据元素,数据元素之间的逻辑关系由元素的存储位置表示
②通常借助程序设计语言的数组类型来实现以学生的基本信息表为例:
假定每个结点(学生记录)占用50个存储单元,数据从0号单元开始由低地址向高地址方向存储,对应的顺序表如下:
-
链式存储
①用任意一组存储单元存储数据元素,无需占用一整块存储空间
②数据元素之间的逻辑关系用指针来表示,即每个结点不仅包含数据元素信息,同时需要增加指针字段,用于存放后继元素的存储地址
同样,以学生信息表为例:
每个数据元素除了包含学生自身的一些信息外,还需要存储下一个数据元素的地址
-
1.2.3 数据类型和抽象数据类型
-
数据结构(Data Type)
基本数据类型:char int float double ···
构造数据类型:数组、结构体、共用体、枚举···
指针、空(void)类型
自定义数据类型(如:用typedef)注意:数组、字符串可以直接用数据类型表示
- 数据类型的作用
① 约束变量或常量的取值范围
② 约束变量或常量的操作(operation)
数据类型:性质相同的值的集合 + 定义在值集合上的一组操作
- 数据类型的作用
-
抽象数据类型(Abstract Data Type, ADT)
从具体问题中抽象出来的数学模型,以及定义在该模型上的一组抽象运算(相关操作) ,不考虑在计算机中的具体存储结构与运算时的具体实现的算法-
抽象数据类型的形式定义:
可用三元组(D,S,P) 表示
D:数据对象
S:D上的关系集合
P:对D的基本操作的集合 -
定义格式:
其中,数据对象、数据关系的定义采用伪代码(数学符号和自然语言)描述,
基本操作的定义格式为:
基本操作有两种参数:
①赋值参数:只为操作提供输入值
②引用参数:以“&”打头,除可以提供输入值外,还将返回操作结果初始条件:可以为空
示例:定义圆
ADT Circle{ 数据对象:D = {r, x, y | r, x, y均为实数} 数据关系:R = {<r, x, y> | r是半径, <x, y>是圆心坐标} 基本操作: Circle(&C, r, x, y) 操作结果:构造一个圆。 double Area(C) 初始条件:圆已存在。 操作结果:计算得到圆的面积。 double Circumference(C) 初始条件:圆已存在。 操作结果:计算得到圆的周长 } ADT Circle
-
1.3 抽象数据类型表示与实现
说明1:抽象数据类型的概念与面向对象方法的思想一致,抽象数据类型独立于具体的实现,将数据和操作封装在一起,使得用户程序只能通过抽像数据类型定义的某些操作来访问其中的数据,从而实现了信息隐藏。在C++中用类的声明来表示抽象数据类型,用类的实现来实现抽象数据类型。
说明2:抽象数据类型相当于在概念层(或称抽象层)上描述问题,而类相当于在实现层描述问题。
- 以复数为例,给出一个完整的抽象数据类型的定义、表示和实现。
(1)定义部分
(2)复数的表示ADT Complex{ D = {r1, r2 | r1, r2均为实数} S = {<r1, r2> | r1为实部,r2为虚部} Assign(&Z, v1, v2) 初始条件:空的复数已存在 操作结果:构造复数Z,r1, r2分别被赋以参数v1, v2的值 Destroy(&Z) 初始条件:复数已存在 操作结果:复数Z已被销毁 GetReal(Z, &realPart) 初始条件:复数已存在 操作结果:返回复数Z的实部 GetImag(Z, &imagPart) 初始条件:复数已存在 操作结果:返回复数Z的虚部 Add(z1, z2, &sum) 初始条件:z1, z2是复数 操作结果:sum返回两个复数z1, z2的和 }ADT Complex
(3)复数的实现部分typedef struct{ float realPart; //实部 float imagPart; //虚部 }Complex
void assign(Complex *A, float real, float imag){ //构造一个复数 A -> realPart = real; A -> imagPart = imag; } void add(Complex *c, Complex A, Complex B){ c ->realPart = A.realPart + B.realPart c ->imagPart = A.imagPart + B.imagPart } ······
1.4 算法与算法分析
1.4.1 算法的定义及特性
- 算法的定义
① 对特定问题求解方法和步骤的一种描述
② 是指令的有限长序列
③ 每条指令表示一个或者多个操作 - 算法的描述
自然语言:英语、中文······
流程图:传统的流程图、NS流程图······
伪代码:类语言(如类C语言)
程序代码:C语言、Java语言、Python······ - 算法与程序
算法:解决问题的方法,一个问题可能有多种解决方法,即有多种算法
程序:用某种语言具体实现算法 - 算法的五种特性
1、有穷性:执行有穷步之后可以结束
2、确定性:对于任何情况下执行的操作均不会产生二义性,每个操作在算法中均有确切的规定
3、可行性:所有操作都可以通过已经实现的基本操作运算执行有限次来实现
4、输入:一个算法有零个或者多个输入
5、输出:一个算法有一个或者多个输出
1.4.2 评价算法优劣的基本标准
算法设计的要求:
- 正确性:在合理的输入下,可以在有限的运行时间内得到正确的结果。
- 可读性:有助于人对算法进行理解,难以理解的算法会给调试和修改造成困难。
- 健壮性:对非法输入数据可以做出相应的反应,而不是产生一些奇怪的输出结果。
- 高效性:包括时间、空间两个方面,分别用时间复杂度和空间复杂度衡量。
1.4.3 算法的时间复杂度
衡量算法效率的方法主要有两类:事后统计法和事前分析估算
-
事后统计法
- 做法:先要将算法实现,然后测算其在时间和空间开销
- 缺陷:
①必须将算法转换成可执行的程序
②时间和空间上的开销的测算结果严重依赖计算机的软硬件等环境因素,导致非常容易掩盖算法本身的优劣 - 结论:
由于事后统计法所具有的缺陷,通常采用事前分析估算的方法来衡量算法的效率。
-
事前分析估算
计算机执行一种简单操作(如:赋值、比较、移动等)所需要时间与操作次数的乘积
-
问题规模与语句频度
在不考虑计算机的软硬件等环境因素,影响算法时间代价的最主要因素是问题规模。-
问题规模:算法求解问题输入量的多少,一般用整数 n 表示。
(1)对于不同的问题,问题规模 n 的含义是不同的,例如:集合运算中 n 为元素的个数;排序运算中 n 为参加排序的记录数;在矩阵运算中 n 为矩阵的阶数;在多项式运算中 n 为多项式的项数······
(2)n 越大算法的执行时间越长 -
语句频度:一条语句的重复执行次数
-
一个算法的执行时间
总时间 = 其包含的所有语句执行时间的总和
语句执行时间 = 该条语句的重复执行次数 × 执行一次所需要的时间 -
每条语句执行一次所需时间
语句执行一次实际所需时间与计算机的软硬件等环境因素有关,
所以算法分析并不是精确统计算法实际执行所需要的时间,而是针对算法中语句的执行次数做出统计,
在这个过程中,假定执行每条语句所需时间均为单位时间,从而,一个算法的执行时间可以使用算法中所有语句的频度之和来度量
例如:求两个n阶矩阵的乘积的算法
该算法中,所有语句频度之和,是矩阵阶数的函数,用 f(n) 表示,示例中算法的执行时间与 f(n) 成正比。
-
-
时间复杂度定义
-
基本语句
在求n阶矩阵的乘积的算法中,计算出所有语句的频度,但是在复杂的算法中,这种计算是比较困难的。
所以,为了客观反映算法的执行时间,使用“基本语句”的执行次数来衡量算法的工作过量。所谓基本语句- 所谓基本语句,指的是算法中重复执行次数和算法的执行时间成正比的语句,它对算法的运行时间贡献最大。
- 算法的执行时间随问题规模的增长而增长,因此对算法的评价通常只需要考虑其随问题规模增长的趋势
-
渐近时间复杂度
(1)仅比较数量级
f ( n ) = 2 n 3 + 3 n 2 + 2 n + 1 f(n)=2n^3+3n^2+2n+1 f(n)=2n3+3n2+2n+1 与 n 3 n^3 n3 是同阶的,或者说 f ( n ) f(n) f(n)和 n 3 n^3 n3的数量级(Order of Magnitude)相同。此处,用“O”表示数量级,记作 T ( n ) = O ( f ( n ) ) = O ( n 3 ) T(n)=O(f(n))=O(n^3) T(n)=O(f(n))=O(n3)(2)若有某个辅助函数 f ( n ) f(n) f(n),当 n → ∞ n \rightarrow \infty n→∞时, lim n → ∞ T ( n ) f ( n ) \lim\limits_{n\to\infty}\frac{T(n)}{f(n)} n→∞limf(n)T(n)是一个不等于零的常数,则称 f ( n ) f(n) f(n)是 T ( n ) T(n) T(n)同数量级的函数
- 算法的渐近时间复杂度:
指的是算法中基本语句的执行次数是问题规模 n n n的某个函数 f ( n ) f(n) f(n),并记为 T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n)),简称时间复杂度
- 算法的渐近时间复杂度:
-
-
求算法的时间复杂度的基本方法
(1)基本语句:找出所有语句中语句频度最大的那条语句
(2)计算基本语句的执行次数(频度),得到问题规模 n n n的某个函数 f ( n ) f(n) f(n)
(3)取函数 f ( n ) f(n) f(n)的数量级,去掉低次幂项和最高次幂的系数- 可用级数求和的方法计算基本语句的频度
-
最坏、平均、最好时间复杂度
基本语句的执行次数还与问题的输入数据集有关- 例如,在一维数组a中顺序查找某个值为e的元素,并返回元素的位置
for(i=0; i<n; i++) if(a[i]==e) return i+1; return 0; //最好情况:执行一次就能完成查找 //最坏情况:需要执行n次才能完成
从此算法可以看出,第二条语句的语句频度不仅和问题规模 n n n有关,还与输入数据集中数组 a [ i ] a[i] a[i]的各个元素以及元素e的取值相关。
一般,优先考虑算法的平均时间复杂度,也就是指,在所有可能的情况下,按照输入实例以等概率出现时,算法计算量的加权平均值;
当平均时间复杂度难于计算或者难以确定时,通常考虑算法的最坏情况下的时间复杂度。
1.4.4 算法的空间复杂度
-
渐进空间复杂度——算法所需存储空间的度量
也是问题规模n的函数,记作: S ( n ) = O ( f ( n ) ) S(n)=O(f(n)) S(n)=O(f(n))- 算法所占据的存储空间包含:
- 本身所用的指令、常数、变量和输入数据等
- 对数据进行操作的辅助存储空间
注意:
(1)对于算法输入数据所占的具体存储量取决于问题本身,与算法无关
(2)只需分析算法在实现时所需要的辅助空间即可 - 算法所占据的存储空间包含:
-
例:将一个数组中n个元素逆序存放在原数组当中
算法1:for(i=0; i<n/2; i++){ t=a[i]; a[i]=a[n-i-1]; a[n-i-1]=t; }
算法2:
for(i=0; i<n; i++) b[i]=a[n-i-1]; for(i=0; i<n; i++) a[i]=b[i];
分析:
(1)算法1需要借助一个变量t,与问题的规模大小n无关,所以其空间复杂度为O(1),我们称辅助空间为O(1)的算法为原地工作
(2)算法2需要借助一个大小为n的辅助数组 b b b,所以其空间复杂度为O(n) -
小结
对于一个算法,其时间复杂度和空间复杂度往往相互影响,当追求一个较好的时间复杂度时,可能会占据较多的存储空间,反之亦然。通常,当运算空间较为充足时,均以算法的时间复杂度作为算法优劣的衡量指标。
1.5 小结
本文主要介绍了数据结构的基本概念和术语,以及算法和算法时间复杂度的分析方式。主要内容:
-
1、数据结构是一门研究非数值计算程序设计中操作对象,以及这些对象之间的关系和操作的学科。
-
2、数据结构主要包括:逻辑结构、存储结构。同一逻辑采用不同的存储方式,可以得到不同的存储结构。
-
逻辑结构:从具体问题抽象出来的数学模型,从逻辑关系上描述数据,它与数据的存储无关。根据数据元素之间关系的不同特性,通常有四类基本逻辑结构:集合结构、线性结构、树形结构、图状结构。
-
存储结构:是逻辑结构在计算机中的存储表示,有两类存储结构:顺序存储结构和链式存储结构
-
-
3、抽象数据类型是指由用户定义的、表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称,具体包括三部分:数据对象、数据对象上关系的集合,以及对数据对象的基本操作的集合。
-
4、算法是为解决某类问题而规定的一个有限长的操作序列。算法具有五个特性:有穷性、确定性、可行性、输入和输出。一个算法的优劣可以从以下四个方面评价,分别是正确性、可读性、健壮性和高效性。
-
5、算法分析的两个主要方面是时间复杂度和空间复杂度,以及考察算法的时间效率和空间效率。一般情况下,鉴于运算空间充足,将算法的时间复杂度作为分析重点。