数据结构.docx
- 文档编号:10913907
- 上传时间:2023-02-23
- 格式:DOCX
- 页数:37
- 大小:39.75KB
数据结构.docx
《数据结构.docx》由会员分享,可在线阅读,更多相关《数据结构.docx(37页珍藏版)》请在冰豆网上搜索。
数据结构
数据结构:
是相互之间存在一种或多种特定关系的数据元素的集合,表示为:
(数值或非数值)Data_Structure=(D,S)或:
是指同一数据元素类中各元素之间存在的关系。
亦可表示为:
S=(D,R)或B=(K,R)
数据(data)——所有能被计算机识别、存储和处理的符号的集合(包括数字、字符、声音、图像等信息)。
数据元素(dataelement)——是数据的基本单位,具有完整确定的实际意义(又称元素、结点,顶点、记录等)。
数据项(Dataitem)——构成数据元素的项目。
是具有独立含义的最小标识单位(又称字段、域、属性等)。
三者之间的关系:
数据>数据元素>数据项
数据的逻辑结构:
指数据元素之间的逻辑关系。
即从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。
逻辑结构可细分为4类:
[1]集合结构:
仅同属一个集合[2]线性结构:
一对一(1:
1)[3]树结构:
一对多(1:
n)[4]图结构:
多对多(m:
n)
数据的物理结构:
物理结构亦称存储结构,是数据的逻辑结构在计算机存储器内的表示(或映像)。
它依赖于计算机。
存储结构可分为4大类:
顺序、链式、索引、散列
数据的运算:
在数据的逻辑结构上定义的操作算法。
它在数据的存储结构上实现。
最常用的数据运算有5种:
插入、删除、修改、查找、排序
抽象数据类型定义:
用以下的三元组来表示:
ADT=(D,S,P)
ADT抽象数据类型名{
数据对象:
<数据对象的定义>
数据关系:
<数据关系的定义>
基本操作:
<基本操作的定义>
}ADT抽象数据类型名
算法有5个基本特性:
有穷性、确定性、可行性、输入和输出
算法评价有4个指标:
正确性、可读性、健壮性和效率与低存储量
给定程序计算时间复杂度
逻辑结构唯一,存储结构不唯一,运算的实现依赖于存储结构
线性结构的定义:
若结构是非空有限集,则有且仅有一个开始结点和一个终端结点,并且所有结点都最多只有一个直接前趋和一个直接后继。
可表示为:
(a1,a2,……,an)
线性结构的特点:
①只有一个首结点和尾结点;②除首尾结点外,其他结点只有一个直接前驱和一个直接后继。
线性结构表达式:
(a1,a2,……,an)
线性结构包括线性表、堆栈、队列、字符串、数组等等,其中,最典型、最常用的是------线性表,简言之,线性结构反映结点间的逻辑关系是一对一的
练:
判断下列叙述的正误:
1.数据的逻辑结构是指数据元素之间的逻辑关系,是用户按使用需要建立的。
2.线性表的逻辑结构定义是唯一的,不依赖于计算机。
3.线性结构反映结点间的逻辑关系是一对一的。
4.一维向量是线性表,但二维或N维数组不是。
5.“同一数据逻辑结构中的所有数据元素都具有相同的特性”是指数据元素所包含的数据项的个数都相等。
顺序表的表示:
用一组地址连续的存储单元依次存储线性表的元素,可通过数组V[n]来实现。
把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。
线性表的顺序表示又称为顺序存储结构或顺序映像。
线性表顺序存储特点:
1.逻辑上相邻的数据元素,其物理上也相邻;
2.若已知表中首元素在存储器中的位置,则其他元素存放位置亦可求出(利用数组下标)。
设首元素a1的存放地址为LOC(a1)(称为首地址),
设每个元素占用存储空间(地址长度)为L字节,则表中任一数据元素的存放地址为:
LOC(ai)=LOC(a1)+L*(i-1)
LOC(ai+1)=LOC(ai)+L
注意:
C语言中的数组的下标从0开始,即:
V[n]的有效范围是V[0]~V[n-1]
核心语句:
法1V[i]=V[i-1]+1;
法2V[i]=’a’+i;
法3V[i]=97+i;
顺序表的实现(或操作)
回忆:
数据结构基本运算操作有:
修改、插入、删除、查找、排序
1)修改:
通过数组的下标便可访问某个特定元素并修改之。
核心语句:
V[i]=x;
显然,顺序表修改操作的时间效率是T(n)=O
(1)
2)插入:
在线性表的第i个位置前插入一个元素
实现步骤:
将第n至第i位的元素向后移动一个位置;将要插入的元素写到第i个位置;表长加1。
注意:
事先应判断:
插入位置i是否合法?
表是否已满?
应当有1≤i≤n+1或i=[1,n+1]
核心语句:
for(j=n;j>=i;j--)
a[j+1]=a[j];
a[i]=x;
n++;
3)删除:
删除线性表的第i个位置上的元素
实现步骤:
将第i至第n位的元素向前移动一个位置;表长减1。
注意:
事先需要判断,删除位置i是否合法?
应当有1≤i≤n或i=[1,n]
核心语句:
for(j=i+1;j<=n;j++)
a[j-1]=a[j];
n--;
插入、删除操作平均需要移动一半元素(n/2),插入、删除算法的平均时间复杂度为:
O(n)
1.线性结构(包括表、栈、队、数组)的定义和特点:
仅一个首、尾结点,其余元素仅一个直接前驱和一个直接后继。
2.线性表逻辑结构:
“一对一”或1:
1
线性表存储结构:
顺序、链式
运算:
修改、插入、删除
3.顺序存储特征:
逻辑上相邻,物理上也相邻;
优点:
随机查找快O
(1)
缺点:
插入、删除慢O(n)
1.链式存储结构:
其结点在存储器中的位置是随意的,即逻辑上相邻的数据元素在物理上不一定相邻。
如何实现?
通过指针来实现
2.与链式存储有关的术语
1)结点:
数据元素的存储映像。
由数据域和指针域两部分组成;
2)链表:
n个结点由指针链组成一个链表。
它是线性表的链式存储映像,称为线性表的链式存储结构。
3)单链表、双链表、多链表、循环链表:
结点只有一个指针域的链表,称为单链表或线性链表;有两个指针域的链表,称为双链表;有多个指针域的链表,称为多链表;首尾相接的链表称为循环链表。
4)头指针、头结点和首元结点
3、头指针、头结点和首元结点
头指针是指向链表中第一个结点(或为头结点或为首元结点)的指针;
头结点是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息;
首元结点是指链表中存储线性表第一个数据元素a1的结点。
4.链式存储特点:
逻辑上相邻,物理上不一定相邻
讨论1:
每个存储结点都包含两部分:
数据域和指针域(链域)。
讨论2:
在单链表中,除了首元结点外,任一结点的存储位置由其直接前驱结点的链域指示。
讨论1.在链表中设置头结点有什么好处?
头结点即在链表的首元结点之前附设的一个结点,该结点的数据域中不存储线性表的数据元素,其作用是为了对链表进行操作时,可以对空表、非空表的情况以及对首元结点进行统一处理,编程更方便。
(统一用p-->next表示,而不须特殊处理)
讨论2.如何表示空表?
无头结点时,当头指针的值为空时表示空表;有头结点时,当头结点的指针域为空时表示空表。
讨论3.头结点的数据域内装的是什么?
头结点的数据域可以为空,也可存放线性表长度等附加信息,但此结点不能计入链表长度值。
讨论4.链表的数据元素有两个域,不再是简单数据类型,编程时该如何表示?
因每个结点至少有两个分量,所以要采用结构数据类型。
1.单链表的建立和输出
先开辟头指针,然后陆续为每个数据元素开辟存储空间并赋值,并及时将地址送给前面的指针。
单链表尾结点的指针域要置空!
新手特别容易忘记!
!
小结:
链式存储结构的特点(理解),链式存储的相关术语(理解掌握),链式存储上的建表操作:
头插法,尾插法
2.单链表的修改(或读取)
思路:
要修改第i个数据元素,关键是要先找到该结点的指针p,然后用p->data=new_value即可。
难点:
单链表中想取得第i个元素,必须从头指针出发寻找(顺藤摸瓜),不能随机存取。
核心语句:
见教材P29的GetElem_L函数说明
StatusGetElem_L(LinkListL,inti,ElemType&e)
{p=L->next;j=1;
while(p&&jnext;++j;}
if(!
p||j>i)returnERROR;
e=p->data;
returnOK;}
3.单链表的插入
插入步骤(即核心语句):
Step1:
s->next=p->next;
Step2:
p->next=s;
4.单链表的删除
删除步骤(即核心语句):
q=p->next;//保存b的指针,靠它才能指向c
p->next=q->next;//a、c两结点相连
free(q);//删除b结点,彻底释放
小结:
单链表的实现(理解)1.单链表的修改按序号按值2.单链表的插入3.单链表的删除
6.其它链表形式
讨论1:
用一维数组也能存放链表吗?
怎样实现?
答:
能。
只要定义一个结构类型(含数据域和指示域)数组,就可以完全描述链表,这种链表称为静态链表。
注:
数据域含义与前面相同,指示域相当于前面的指针域。
静态链表的插入与删除操作与普通链表一样,不需要移动元素,只需修改指示器就可以了。
讨论2:
链表能不能首尾相连?
怎样实现?
答:
能。
只要将表中最后一个结点的指针域指向头结点即可(P->next=head;)。
这种形成环路的链表称为循环链表。
特别:
带头结点的空循环链表样式
特点:
1、从任一结点出发均可找到表中其他结点。
2、判定是否结束的操作与单链表不同:
循环条件
单链表-----p=NULL或p->next=NULL循环链表-----p=head或p->next=head
讨论3:
单链表只能查找结点的直接后继,能不能查找直接前驱?
如何实现?
答:
只要把单链表再多开一个指针域即可(例如用*next和*prior;)。
有两个指针的链表称为双向链表。
其特点是可以双向查找表中结点。
参见教材P35—39。
特别:
带头结点的空双向链表样式:
双向链表在非线性结构(如树结构)中将大量使用。
链表的运算效率分析
1.查找:
因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为O(n)。
时间效率分析
2.插入和删除:
因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为O
(1)。
但是,如果要在单链表中进行结点前插入或删除操作,由于要从头查找前驱结点,所耗时间复杂度为O(n)。
小结:
单链表应用举例(理解)
链表的其他形式1.循环单链表(学会)2.双向链表(理解)3.静态单链表(学会)时空效率分析(理解)
问1:
线性表的逻辑结构特点是什么?
其顺序存储结构和链式存储结构的特点是什么?
答:
线性表逻辑结构特点是,只有一个首结点和尾结点;除首尾结点外其他结点只有一个直接前驱和一个直接后继。
简言之,线性结构反映结点间的逻辑关系是一对一(1:
1)的。
顺序存储时,相邻数据元素的存放地址也相邻(逻辑与物理统一);要求内存中可用存储单元的地址必须是连续的。
链式存储时,相邻数据元素可随意存放,但所占存储空间分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针。
问2:
顺序存储和链式存储各有哪些优缺点?
答:
顺序存储的优点是存储密度大(=1),存储空间利用率高;随机存取。
缺点是插入或删除元素时不方便。
链式存储的优点是插入或删除元素时很方便,使用灵活。
缺点是存储密度小(<1),存储空间利用率低;存取元素需顺链查找。
事实上,链表插入、删除运算的快捷是以空间代价来换取时间。
问3:
在什么情况下用顺序表比链表好?
答:
顺序表适宜于做查找这样的静态操作;链表宜于做插入、删除这样的动态操作。
若线性表的长度变化不大,且其主要操作是查找,则采用顺序表;若线性表的长度变化较大,且其主要操作是插入、删除操作,则采用链表。
栈:
与线性表相同,仍为一对一关系。
用顺序栈或链栈存储均可,但以顺序栈更常见。
只能在栈顶运算,且访问结点时依照后进先出(LIFO)或先进后出(FILO)的原则。
基本操作有建栈、判断栈满、栈空、入栈、出栈或读栈顶元素值等。
限定只能在表的一端进行插入和删除运算的线性表(只能在栈顶操作)
问:
堆栈是什么?
它与一般线性表有什么不同?
答:
堆栈是一种特殊的线性表,它只能在表的一端(即栈顶)进行插入和删除运算。
与一般线性表的区别:
仅在于运算规则不同。
栈是仅在表尾进行插入、删除操作的线性表。
表尾(即an端)称为栈顶top;表头(即a1端)称为栈底base
例如:
栈s=(a1,a2,a3,……….,an-1,an)a1称为栈底元素,an称为栈顶元素
插入元素到栈顶(即表尾)的操作,称为入栈。
从栈顶(即表尾)删除最后一个元素的操作,称为出栈。
强调:
插入和删除都只能在表的一端(栈顶)进行!
3.栈的存储结构
表和栈的操作区别——对线性表s=(a1,a2,….,an-1,an)
核心语句:
写入:
v[i]=ai读出:
x=v[i]
压入:
PUSH(an+1)弹出:
POP(x)
前提:
一定要预设栈顶指针top!
an+1
出栈操作(注意要遵循“后进先出”原则)核心语句:
Pop();
Pop();
Printf(Pop());
例1:
一个栈的输入序列是12345,若在入栈的过程中允许出栈,则栈的输出序列43512可能实现吗?
12345的输出呢?
43512不可能实现,主要是其中的12顺序不能实现;12345的输出可以实现,只需压入一个立即弹出一个即可。
例2:
如果一个栈的输入序列为123456,能否得到435612和135426的出栈序列?
435612中到了12顺序不能实现;135426可以实现。
例3(严题集3.1)一个栈的输入序列为123,若在入栈的过程中允许出栈,则可能得到的出栈序列是什么?
可以通过穷举所有可能性来求解:
①1入1出,2入2出,3入3出,即123;②1入1出,2、3入3、2出,即132;③1、2入,2出,3入3出,即231;④1、2入,2、1出,3入3出,即213;⑤1、2、3入,3、2、1出,即321;合计有5种可能性。
例4:
计算机系考研题(华中科大)设依次进入一个栈的元素序列为c,a,b,d,则可得到出栈的元素序列是:
A)a,b,c,dB)c,d,a,bC)b,c,d,aD)a,c,d,b
A、D可以(B、C不行)。
讨论:
有无通用的判别原则?
答:
有。
若借助栈由输入序列12…n得到的输出序列为P1P2…Pn,则在输出序列中不可能出现这样的情形:
存在着i 补充1: 若入栈动作使地址向高端增长,称为“向上生成”的栈;若入栈动作使地址向低端增长,称为“向下生成”的栈; 对于向上生成的栈: 入栈口诀: 堆栈指针top先压后加(v[top++]=x);出栈口诀: 堆栈指针top先减后弹(y=v[--top])。 补充2: 栈不存在的条件: base=NULL;栈为空的条件: base=top;栈满的条件: top-base=stacksize; 说明: ①链栈不必设头结点,因为栈顶(表头)操作频繁;②采用链栈存储方式,可使多个栈共享空间;当栈中元素个数变化较大,且存在多个栈的情况下,链栈是栈的首选存储方式。 问: 为什么要设计堆栈? 它有什么独特用途? 答: 调用函数或子程序非它莫属;递归运算的有力工具;用于保护现场和恢复现场;简化了程序设计的问题。 数制转换(十转N)——P48设计思路: 用栈暂存低位值 例2: 括号匹配的检验————P49设计思路: 用栈暂存左括号 例3: 表达式求值—-————P52设计思路: 用栈暂存运算符 例4: 汉诺仪(Hanoi)塔-——P55设计思路: 用栈实现递归调用 小结: 与线性表相同,仍为一对一关系。 用顺序栈或链栈存储均可,但以顺序栈更常见只能在栈顶运算,且访问结点时依照后进先出(LIFO)或先进后出(FILO)的原则。 关键是编写入栈和出栈函数,具体实现依顺序栈或链栈的不同而不同。 基本操作有建栈、判断栈满、栈空、入栈、出栈或读栈顶元素值等。 限定只能在表的一端进行插入和删除运算的线性表(只能在栈顶操作) 栈与递归 当一个函数调用另一个函数时,在调用之前系统要做3件事: 1.将所有的实在参数、返回地址等信息传递给被调用函数保存;2.为被调用函数的局部变量分配存储空间;(入栈)3.将控制转移到被调用函数的入口。 返回时,系统要做3件事: 1.保存被调用函数的计算结果;2.释放被调用函数的数据区;(出栈)3.依照被调用函数保存的地址将控制转移到调用函数。 1.递归: 递归定义: 简单地说,一个用自己定义自己的概念,称为递归定义。 2.递归函数: 一个直接调用自己或通过一系列调用间接调用自己的函数称为递归函数。 3.递归算法的编写 1)将问题用递归的方式描述(定义) 2)根据问题的递归描述(定义)编写递归算法 问题的递归描述(定义) 递归定义包括两项 基本项(终止项): 描述递归终止时问题的求解; 递归项: 将问题分解为与原问题性质相同,但规模较小的问题; 例: n! 的递归定义 基本项: n! =1当n=1 递归项: n! =n(n-1)! 当n>1 intfact(intn) {//算法功能是求解并返回n的阶乘 if(n==1)return (1); elsereturn(n*fact(n-1)); } 队列 与同线性表相同,仍为一对一关系。 顺序队或链队,以循环顺序队更常见。 只能在队首和队尾运算,且访问结点时依照先进先出(FIFO)的原则。 关键是掌握入队和出队操作,具体实现依顺序队或链队的不同而不同。 基本操作有入队或出队,建空队列,判队空或队满等操作。 只能在表的一端进行插入运算,在表的另一端进行删除运算的线性表(头删尾插) 队列(Queue)是仅在表尾进行插入操作,在表头进行删除操作的线性表。 表尾即an端,称为队尾;表头即a1端,称为队头。 它是一种先进先出(FIFO)的线性表。 例如: 队列Q=(a1,a2,a3,….,an-1,an)插入元素称为入队;删除元素称为出队。 怎样实现入队和出队操作? 入队(尾部插入): rear->next=S;rear=S; 出队(头部删除): front->next=p->next; 空队列的特征? 约定: front=rear 队列会满吗? front=rear,一般不会,因为删除时有free动作。 除非内存不足! 入队(尾部插入): v[rear]=e;rear++; 出队(头部删除): x=v[front];front++; 问: 什么叫“假溢出”? 如何解决? 答: 在顺序队中,当尾指针已经到了数组的上界,不能再有入队操作,但其实数组中还有空位置,这就叫“假溢出”。 解决假溢出的途径———采用循环队列 新问题: 在循环队列中,空队特征是front=rear;队满时也会有front=rear;判决条件将出现二义性! 解决方案有三: ①加设标志位以区别队列是“空”还是“满”;②使用一个计数器记录队列中元素个数(即队列长度);③人为浪费一个单元,令队满特征为front=(rear+1)%N; 队空条件: front=rear(初始化时: front=rear) 队满条件: front=(rear+1)%N(N=maxsize) 队列长度: L=(N+rear-front)%N 选用空闲单元法: 即front和rear之一指向实元素,另一指向空闲元素。 问: 在具有n个单元的循环队列中,队满时共有多少个元素? n-1个 例: 数组Q[n]用来表示一个循环队列,f为当前队列头元素的前一位置,r为队尾元素的位置。 假定队列中元素的个数小于n,计算队列中元素的公式为: (A)r-f(B)(n+f-r)%n(C)n+r-f(D)(n+r-f)%n 4种公式哪种合理? 当r≥f时(A)合理;当r 例3: 在一个循环队列中,若约定队首指针指向队首元素的前一个位置。 那么,从循环队列中删除一个元素时,其操作是先移动队首指针,后取出元素。 讨论: 线性表、栈与队的异同点? 相同点: 逻辑结构相同,都是线性的;都可以用顺序存储或链表存储;栈和队列是两种特殊的线性表,即受限的线性表(只是对插入、删除运算加以限制)。 不同点: ①运算规则不同,线性表为随机存取,而栈是只允许在一端进行插入和删除运算,因而是后进先出表LIFO;队列是只允许在一端进行插入、另一端进行删除运算,因而是先进先出表FIFO。 ②用途不同,线性表比较通用;堆栈用于函数调用、递归和简化设计等;队列用于离散事件模拟、多道作业处理和简化设计等。 问: 为什么要设计队列? 它有什么独特用途? 离散事件的模拟(模拟事件发生的先后顺序);操作系统中多道作业的处理(一个CPU执行多个作业); 补充: 递推与递归的区别: 递推: 由“小”到“大”递进;递归: 由“大”到“小”嵌套。 例如: 求f(n)=n! ①递推法: Fact=1;for(i=1;i<=n;i++)Fact*=i; ②递归法: longintfact(n) {intn; longf; if(n>1)f=n*fact(n-1); elsef=1; return(f); } 补充: C语言中常用的串运算 串比较,intstrcmp(chars1,chars2);//StrCompare(S,T) 求串长,intstrlen(chars);//StrLength(S) 串连接,charstrcat(charto,charfrom)//Concat(&T,S1,S2) 子串T定位,charstrchr(chars,charc);//Index(S,T,pos) Concat=concatenation,在字符串处理中,把多个短字符串合成为长字符串的操作。 注: 用C处理字符串时,要调用标准库函数#include 串类型的定义: 记为: s=‘a1,a2,……..,an’(n≥0) 串即字符串,是由零个或多个字符组成的有限序列,是数据元素为单个字符的特殊线性表。 若干术语: 串长: 串中字符个数(n≥0).n=0时称为空串。 空白串: 由一个或多个空格符组成的串。 子串: 串s中任意个连续的字符序列叫s的子串;S叫主串。 子串位置: 子串的第一个字符在主串中的第一个位序。 字符位置: 字符在串中的序号。 串相等: 串长度相等,且对应位置上字符相等。 隐含结束符‘/0’,即ASCII码NUL 练1: 串是由0个或多个字符组成的序列,一般记为S=’a1a2……an’。 练2: 现有以下4个字符串: a=‘BEI’b=‘JING’c=‘BEIJING’d=‘BEIJING’问: ①他们各自的长度? ②a是哪个串的子串? 在主串中的位置是多少? a=3,b=4,c=7,d=8;a是c和d的子串,在c和d中的位置都是1 练3: 空串和空白串有无区别? 答: 有区别。 空串(NullString)是指长度为零的串;而空白串(BlankString),是指包含一个或多个空白字符‘’(空格键)的字符串. 首先强调: 串与线性表的运算有所不同,是以“串的整体”作为操作对象,例如查找某子串,在主串某位置上插入一个子串等。 串有三种机内表示方法: 定长顺序存储表示
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构