文章目录
- 一、典型数据结构介绍
- 1.1 基本概念和术语
- 1、基本数据概念
- 2、抽象数据类型
- 3、算法
- 4、算法复杂度
- 5、数据结构
- 二、数据的存储结构
- 2.1 线性结构
- 1、线性表(一般线性表)
- 2、栈和队列(受限线性表)
- 1) 栈 Stack
- 2) 队列 Queue
- 3、串
目标:
- 掌握典型的数据结构
- 掌握软件开发中存储对象的定义方法
- 掌握数据结构与算法基础应用
- 掌握业务逻辑的算法设计与选择方法
一、典型数据结构介绍
数据结构是介于数学、计算机硬件和计算机软件三者之间的一门核心课程,数据结构不仅是一般程序设计(特别是非数值计算的程序设计)的基础,而且是设计和实现编译程序、操作系统数据库系统及其它系统程序和大型应用程序的重要基础。
1.1 基本概念和术语
1、基本数据概念
数据(data): 是对客观事物的符号表示,指所有能输入到计算机中并被计算机程序处理的符号的总称。计算机输入和处理的数据除数值外,还有字符串、表格、图像甚至声音等,它们都是数字编码范畴。
数据元素(data element) :数据的基本单位,在计算机程序中通常作为一个整体,一个数据元素可以由若千个数据项组成,也可以只由一个体进行考虑和处理。数据项组成。数据元素又被称为元素、结点(node)或记录(record)。
数据项(data item): 指数据的不可分割的、含有独立意义的最小单位,数据项有时也称字段(field)或域。(如表中的列)
数据对象(Data object) : 是性质相同的数据元素的集合,是数据的一个子集。如Boolean={false,true}, Digit={0,1,2,3,4,5}。它们都是数据对象,是一组实例或值。数据对象的实例要么是一个原语(primitive或atomic),要么是由其他数据对象的实例组合而成。对后一种情况我们称对象实例的单个组件为元素(element)。
2、抽象数据类型
抽象数据类型(Abstract Data Type 简称ADT): 表示(类C语言表示)
- 抽象数据类型的概念与程序设计语言中的数据类型概念相同。抽象数据类型更为广泛,可以自己定义数据类型。
- 抽象数据类型只是一个数学模型以及定义在模型上的一组操作。通过对数据的抽象,定义了数据的取值范围以及对数据操作的集合
- 抽象数据类型的特征是实现与操作分离,从而实现封装,抽象数据类型体现了程序设计中问题分解和信息隐藏的特征。
- “抽象”的意义在于数据类型的数学抽象特性
ADT抽象数据类型名[
数据对象:<数据对象的定义>
数据关系:<数据关系的定义>
基本操作:<基本操作的定义>
}
3、算法
1、定义
指一系列确定的而且是在有限步骤内完成的操作。特点如下:
- 有穷性:能执行结束
- 确定性:对于相同的输入执行相同的路径
- 0至多个输入
- 1多个输出
- 有效性(可行性)(用于描述算法的操作都是足够基本的)
2、算法与数据结构的关系
计算机科学家沃斯(N.Wirth)提出的:”“程序 =数据结构 + 算法“
,一个算法总是建立在一定数据结构上的;反之,算法不确定,就无法决定如何构造数据。
3、算法设计的要求
- 正确性:四层含义: (1)无语法错误; (2) 一般输入得出满足规格说明要求的结果; (3)边界、苛刻数据能产生满足规格说明要求的结果, (4)一切合法输入都能产生满足规格说明要求的结果。
- 可读性:首先是给人读,然后才是机器执行
- 健壮性:容错性
- 效率与低存储量需求
4、算法的效率:
一个算法如果能在所要求的·
资源限制
内将问题解决好,则称这个算法是有效率的。.例如,一个资源限制是:可用来存储数据的全部空间一-可能是分离的内存空间限制和磁盘空间限制以及允许执行每一个子任务所需要的时间。
5、算法的性能的评价:
- 算法所需的计算时间
- 算法所需的存储空间
- 算法的简单性
4、算法复杂度
衡量算法的标准有时间和空间两个维度,具体到评价标准为时间复杂度和空间复杂度
1、算法的时间复杂度
定义:一般情况下,算法中基本操作重复执行的时间是问题规模n的某个函数f(n),算法的时间量度记作
T(n) = O(f(n)) “大O记法
它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称时间复杂度
- 在最后的计算中,去掉低阶项,常数项,所以最终得到O(n3)
2、复杂度的描述
由于算法的时间复杂度分析只考虑相对于问题规模n的增长率,因而,在难以精确计算基本操作执行次数的情况下,只要求出它关于n的增长率即可。
我们可以在计算任何算法运行时间代价时,忽略所有的常数和低次项:
常量阶: 时间复杂度为O(1)
线性阶: 时间复杂度为O(n)
平方阶: 时间复杂度为O(n2)
对数阶: O(log2n)
指数阶: O(2n)
例:如果某算法的运行时间代价为O(5n2 + n),可以忽略其中的低次项和常数,而视该算法的时间复杂度为O(n2)。
3、时间复杂度的等级
以下六种计算算法时间的多项式是最常用的。其关系为:Gamma公式展示
O
(
1
)
<
O
(
l
o
g
n
)
<
O
(
n
)
<
O
(
n
l
o
g
n
)
<
O
(
n
2
)
<
O
(
n
3
)
O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)
指数时间的关系为:
O
(
2
n
)
<
O
(
n
!
)
<
O
(
n
n
)
\ O(2^n)<O(n!)<O(n^n)
O(2n)<O(n!)<O(nn)
5、数据结构
数据结构(Data Structure):是相互之间存在一种或多种特定关系的数据元素的集合。
- 形式定义:
- 数据结构是一个二元组Data Structure =( D,S)
- 其中:D是数据元素的有限集,S是D上关系的有限集
数据结构包括逻辑结构和物理结构两个层次。
- 逻辑结构是面向问题的:
- 物理结构是面向计算机的
1)数据的逻辑结构
数据元素之间存在的一种或多种特定的关系被称为数据的逻辑结构。通常有四类基本结构:集合、线性结构、树形结构、图状结构
2)数据的物理结构
物理结构,又称存储结构。数据的存储结构是逻辑结构在计算机存储器中的实现。数据元素在计算机中主要有两种不同的存储方法: 顺序存储结构和链式存储结构
- 顺序存储结构:借助元素在存储器中的相对位置来表示C数据元素之间的逻辑关系(公式化描述)
- 链式存储结构:借助指示元素存储地址的 指针表示数据元素之间的逻辑关系(链式描述)
二、数据的存储结构
2.1 线性结构
1、线性表(一般线性表)
线性表(Linear List),线性表(Linear List)是n(n20)个数据元素的有限序列,表中各个数据元素具有相同的特性,既属于同一数据对象,表中相邻的数据元素之间存在“序偶”关系。是最简单,也是最基本的一种线性数据结构
通常将线性表记做
( a 1 , a 2 . . . , a i − 1 , a i , a i + 1 , . . . , a n − 1 , a n ) \ (a_1, a_2 ..., a_{i-1} ,a_i, a_{i+1}, ..., a_{n-1}, a_n ) (a1,a2...,ai−1,ai,ai+1,...,an−1,an)
表中 a i − 1 \ a_{i-1} ai−1领先于 a i \ a_i ai, a i \ a_i ai领先于 a i + 1 \ a_{i+1} ai+1,称 a i − 1 \ a_{i-1} ai−1是 a i \ a_i ai的直接前驱元素, a i + 1 \ a_{i+1} ai+1是 a i \ a_i ai的直接后继元素n是表的长度。
当n=0时,表为空。a是第一个数据元素,a是最后一个数据元素,a;是第i个数据元素其中i为数据元素a在线性表中的位序
除了这种优先关系之外,在线性表中不再有其他的结构
例1:简单的线性表
例2:复杂点的线性表如下,每一行的数据为一个数据元素
线性表的存储结构:
-
顺序存储
- 内存地址连续
- 支持随机存取,适用于频繁查询
- 节约空间
-
链式存储
- 队了存数据外还会额外一个空间用于存储指针
- 内存地址不一定连续
- 适用于频繁插入、删除
- 存储密度低
-
线性表抽象 数据类型定义:
ADT List{
数据对象:D={ai |ai ∈ ElemSet,i=1,2,...,n,n>0}
数据关系:R1={<ai-1,ai lai-1,aieD,i=2,...n}
基本操作:
1) lnitList (&L): 操作结果:将L初始化为空表
2) DestroyList(&L) : 操作前提:线性表L已存在。操作结果:将L销毁
3) ClearList(&L): 操作前提:线性表L已存在。操作结果:将表L置为空表。
4) EmptyList (L): 操作前提:线性表L已存在.操作结果: 如果L为空表则返回真,否则返回假
5) ListLength (L): 操作前提:线性表L已存在。操作结果: 如果L为空表则返回0,否则返回表中的元素个数
6) Getltem(L,i,&e): 操作前提: 表L已存在,1<=i<=listlength(L)。操作结果: 用e返回L中第i个元素的值。
7) LocateElem(L,e):操作前提: 表L已存在,e为合法元素值。操作结果: 如果L中存在元素e,则将“当前指针”指向元素e所在位置并返回真,否则返回假
8)ListInsert(&Lie):操作前提: 表L已存在,e为合法元素值且1<i<ListLength(L)+1。操作结果: 在L中第i个位置之前插入新的数据元素e,L的长度加1.
9)PriorElem( L,cur e, &pre e):如果cur e是L中的元素,并且不是第一个,则用pre e返回它的前驱。否则失败。
10) NextElem( L,cur e,&next e):如果cur e是L中的元素,并且不是最后一个,则用next e返回它的后继。否则失败。
11)ListDelete(&L,i,&e):操作前提: 表L已存在且非空,1<iListLength(L)。操作结果: 删除L的第i个数据元素,并用e返回其值,L的长度减1
}ADT List
- 利用上述定义的线性表类型,可以实现其它更复杂的操作,例如:归并、拆分、复制、排序。
2、栈和队列(受限线性表)
栈和队列是两种重要的线性结构。从数据结构的角度看,栈和队列是操作受限的线性表:
1) 栈 Stack
栈:限定只能在表的一端进行插入和删除的特殊的线性表
- 栈顶(top):允许插入和删除的一端(约定top始终指向新数据元素将存放的位置)
- 栈底(bottom):不允许插入和删除的一端
- 压入(进栈)是增加数据,弹出(出栈)是删除数据
- 栈特性:
后进先出
(Last In First Out),简称为LIFO线性表 - 应用场景: 进制转换、括号匹配
栈的抽象数据类型定义:
栈的基本操作 - InitStack(&S) : 操作结果:构造一个空的栈S.
- DestroyStack(&S): 初始条件:栈S已经存在,操作结果: 销毁栈S。
- ClearStack(&S):初始条件: 栈S已经存在,操作结果: 将栈S重置为空栈
- StackEmpty(S):初始条件:栈S已经存在,操作结果: 若栈S为空栈,则返回TURE;否则返回FALSE
- StackLength(S):初始条件: 栈S已经存在,操作结果:返回栈S中的数据元素个数
- GetTop(S,&e):初始条件: 栈S已经存在且非空,操作结果: 用e返回栈S中栈顶元素的值
- Push(&S,e):入栈操作,初始条件:栈S已经存在,操作结果: 插入元素e为新的栈顶元素
- Pop(&S&e):出栈操作,初始条件:栈S已经存在且非空,操作结果: 删除S的栈顶元素并用e返回其值。
- StackTraverse(S, visit ():初始条件:栈S已经存在且非空,操作结果:从栈底到栈顶依次对S的每个元素调用函数 visit 0。一旦visit (失败,则操作失败
常用操作:初始化栈InitStack(S)、入栈 Push(&S,e)、 出栈 Pop(&S,&e)、获取栈顶元素内容 、GetTop(S,&e)、判断栈是否为空StackEmpty(S)
栈的物理结构:
2) 队列 Queue
队列(queue)是一种先进先出(FIFO)的线性表,它只允许在表的一端–称为队尾(rear)进行插入操作,而在另一端一称为队头(front)进行删除操作。如图所示
队列(queue)的场景:: 排队取款、排队购物、排队上车、排队打饭等;WEB客户端的请求在服务器端的排队。
队列 的抽象数据类型定义:
队列的基本操作:
- InitQueue(&Q):操作结果:构造一个空的队列Q
- DestroyQueue(&Q):初始条件: 队列Q已经存在。、操作结果: 销毁队列Q
- ClearQueue(&Q):初始条件:队列Q已经存在、操作结果: 将队列Q清为空队列
- EnQueue(&Q,e):入队操作,初始条件:队列Q已经存在。操作结果:插入元素e为新的队尾元素
- DeQueue(&Q,&e):出队操作,初始条件:队列Q已经存在且非空、操作结果:删除Q的队头元素,并用e返回其值
- QueueTraverse(Q,visit ()):遍历操作,初始条件:队列Q已经存在且非空,操作结果:从队头到队尾依次对Q的每个元素调用函数visit 0。一旦visit 0失败,则操作失败
3、串
串是由零个或多个字符组成的有限序列,记作: S = ′ c 1 c 2 c 3 . . . c n ′ \ S='c_1c_2c_3...c_n' S=′c1c2c3...cn′
其中,S是串名字c1c2c3…cn’是串值,ci是串中字符,n是串的长度,表示串中字符的数目
空串:零个字符的串称为空串
子串:串中任意个连续的字符组成的子序列
主串:包含子串的串
空串是任意串的子串,任一串是它自身的子串。除它本身外一个串的其它子串都是它的真子串
例:假设a、b、c、d为如下4个串:a=BEl’ b=’JING‘ c=BEIJING d=BEIJING
a,b是C的子串,也是d的子串,但c不是d的子串
串的抽象数据类型定义:
串的基本操作:
- StrAssign (&T, chars):初始条件: chars 是字符串常量。操作结果: 把 chars 赋为T的值
- StrCopy(&T, S):初始条件: 串 S存在,操作结果: 由串 S 复制得串 T。
- DestroyString (&S):初始条件:串 S存在,操作结果: 串 S被销毁
- StrEmpty (S):初始条件:串 S存在,操作结果: 若S 为空串,则返回true,否则返回 false
- StrCompare (S, T):初始条件:串 S和T存在,操作结果: 若S >T,则返回值>0;若S = T,则返回值= 0;若S< T,则返回值< 0
- StrLength (S):初始条件: 串 S存在.操作结果:返回S的素个数,称为串的长度
- Concat(&T,S1, S2):初始条件: 串 S1和S2存在、操作结果: 用T返回由S1和S2联接而成的新串
- SubString (&Sub,S, pos, len):初始条件: 串S 存在,1posStrLength(S)且 O<len<StrLength(S) - pos+1;操作结果: 用Sub返回串S的第 pos 个字符起长度为len的子串
- Index(S, T pos):初始条件:串S和T存在,T是非空串,1<pos<StrLength(S);操作结果:若主串S中存在和串T值相同的子串则返回它在主串 S中第pos个字符之后第一次出现的位置;否则函数值为0。
- Replace (&S,T V):初始条件: 串ST和V 均已存在,且T是非空串。操作结果:用V替换主串S中出现的所有与(模式串)T相等的不重叠的子串。
- Strlnsert(&S, pos,T):初始条件:串S和T存在,1<pos<StrLength(S)+1。操作结果: 在串S的第pos个字符之前插入串T
- StrDelete (&S, pos, len):初始条件: 串S存在,1<pos<StrLength(S)-len+1.。操作结果: 从串S中删除第pos个字符起长度为len的子串
- ClearString(&S):初始条件:串S存在,操作结果:将S清为空串