第章数据结构基础新.docx
- 文档编号:29460270
- 上传时间:2023-07-23
- 格式:DOCX
- 页数:53
- 大小:298.67KB
第章数据结构基础新.docx
《第章数据结构基础新.docx》由会员分享,可在线阅读,更多相关《第章数据结构基础新.docx(53页珍藏版)》请在冰豆网上搜索。
第章数据结构基础新
第13章数据结构基础
13.1概述
利用计算机进行数据处理是计算机应用的一个重要领域。
数据是对客观事物的符号表示,是由大量的数据元素组成的。
这些元素之间具有一定的逻辑关系,需要存放在计算机中。
因此,数据在计算机中如何组织,以便高效率地处理,节省存储空间,是数据处理的关键问题。
了解和掌握数据结构的基本思想和方法,对充分发挥计算机的效能,为应用所涉及的数据选择适当的逻辑结构、存储结构以及相应的算法,编制出高质量的程序是很有帮助的。
13.1.1数据结构的基本概念
数据是指所有能输入到计算机中并能被计算机程序处理的符号的集合。
如数值、字符、声音、图像等。
数据元素是组成数据的基本单位,在计算机中通常作为一个整体进行处理。
一个数据元素可由一个或多个数据项组成,数据项是有独立含义的数据的最小单位,也称为结点或记录。
例如学生学籍表是数据,每一名学生的记录就是一个数据元素。
数据对象是性质相同的数据元素的集合,是数据的一个子集。
例如,集合N={0,±1,±2,…}表示的数据对象是全体整数。
数据类型是指一个值的集合和定义在该集合上的一组操作的总称。
例如C语言中的整数类型,其值集为某一区间的整数,定义在其上的一组操作为:
加、减、乘、除和取模等算术运算。
数据结构是指相互之间存在一种或多种特定关系的数据元素集合。
在任何问题中,数据元素都不是孤立存在的,在它们之间存在着某种关系,这种数据元素相互之间的关系就是结构。
定义一个数据结构由两部分组成,一是数据结构中数据元素的集合;另一个是数据结构中所有关系的集合。
通常可用一个二元组来表示:
B=(D,R),D是数据元素的有限集,R是D上关系的有限集。
例如,英文26个字母表的数据结构用二元组可表示为:
B={D,R}
D={a,b,c,·······,x,y,z}
R={(a,b),(b,c),……,(y,z)}
此例中的数据元素是简单项。
数据结构是一门研究数据的组织、存储和运算的一般方法的学科,应包括三个方面的内容,即研究数据的逻辑结构和存储结构以及它们之间的相互关系,并对这种结构定义相应的运算。
数据的逻辑结构是指只抽象地反映数据元素之间的逻辑上联系的结构,与数据的存储无关。
存储结构又称为物理结构,是指数据在计算机中的存储方式。
如图13-1所示。
图13-1数据结构研究的三个方面问题
1.数据的逻辑结构
数据的逻辑结构,反映数据元素的逻辑关系。
主要有四种基本数据结构:
(1)集合:
数据元素之间同属于一个集合,别无其他关系;
(2)线性结构:
结构中的数据元素之间存在一对一的线性关系;
(3)树形结构:
结构中的元素之间存在着一对多的层次关系;
(4)图形结构或网状结构:
结构中的元素之间存在着多对多的任意关系。
图13-2(a)(d)给出了这四种基本结构的关系图。
(a)集合(b)线性结构(c)树结构(d)图结构
图13-2四种基本结构的关系图
2.数据的存储结构
数据的存储结构是指数据元素在计算机中以什么方式存储,也称为物理结构,是逻辑结构在计算机中的存储映像和实现,它包括数据元素的表示和关系的表示。
数据的存储结构主要有两大类:
顺序存储结构和非顺序存储结构。
顺序存储结构是指用一组连续的存储单元来存放具有某种结构的数据元素,也称为结点。
它把逻辑上相邻的数据元素存储在物理上相邻的存储单元里,结点之间的关系由存储单元的邻接关系来体现。
非顺序存储结构通常采用链式存储结构,用任意的存储单元来存放结点,因此,每个结点中至少包含一个指针域,数据元素之间逻辑上的联系是由指针来体现的。
这种存储结构可以把逻辑上相邻的两个元素存放在物理上不相邻的存储单元中。
3.数据的操作
数据的操作就是对数据进行的处理,讨论数据结构的目的就是在计算机中实现操作。
数据在计算机内的有效组织将直接影响数据处理的效率,数据结构就是研究数据的表示及其相关的运算操作。
13.1.2算法的基本概念
1.算法定义
用计算机解决一个实际问题,首先应进行程序设计,而程序设计主要包括算法的设计和数据结构的设计。
“算法”与“数据结构”是密不可分的关系。
算法是规则的有限集合,是对特定问题求解步骤的一种逻辑描述。
一个算法应该具有如下特性:
(1)有穷性:
算法必须在执行有穷步之后结束。
(2)确定性:
算法的每一步必须是确切定义的,无二义性。
(3)可行性:
算法的每一步都是能够实现的,即可操作的。
(4)输入:
算法有多个或0个输入。
(5)输出:
算法至少有1个或若干个输出。
设计一个“好”的算法,通常要考虑达到以下目标:
(1)正确性:
所谓正确是指算法应当满足具体问题的要求,对于一切合法的输入都能产生满足规格说明要求的结果。
(2)易读性:
算法主要是为了人的阅读与交流,尤其是在信息化的今天尤为重要,所以要求算法要有助于人们对算法的理解,以便更好的推广使用,让其充分发挥效益。
(3)健壮性:
是指当输入非法数据时,算法也能适当地作出反应或进行处理,而不会产生莫明其妙的输出结果,更不会死机和死循环。
(4)高效率和低存储量:
所谓效率,指的是执行时间,即对于同规模的问题,哪种算法所用的时间少,哪种算法就效率高。
存储量需求是指解决相同规模的问题,所需的最大存储空间。
哪种算法所用的空间少,就相对是好的算法。
2.算法的性能评价
算法的性能评价是对问题规模与该算法在运行时所占用的空间与所耗费的时间给出一个数量关系的评价,用时间复杂度和空间复杂度度量。
(1)算法的时间复杂度
算法的时间复杂度不是算法执行时间的精确度量,而是用来评估算法的时间增长趋势,只与问题规模有关。
应当抛弃具体机器条件,仅考虑算法本身的效率高低。
为便于比较解决同一问题的不同算法的性能,通常以算法中基本操作重复执行的频度作为算法的时间度量标准。
记作:
T(n)=(f(n))T(n)是问题规模的函数,表示数量级
随问题规模n的增大,算法执行时间的增长率T(n)和f(n)的增长率相同。
简称时间复杂度。
通常应选择算法内重复执行次数最多的基本语句,作为算法执行时间量度。
一般情况下是最深层循环内的基本语句。
[例13-1]x+=5;
单个语句的频度为1,则程序段的时间复杂度为T(n)=
(1)
[例13-2]两个n×n阶矩阵相乘。
算法中单个语句的执行次数
for(i=0;i for(j=0;j {c[i][j]=0;n2 for(k=0;k c[i][j]=c[i][j]+a[i][k]*b[k][j];n3 } T(n)=2n3+2n2+n=(2n3+2n2+n),T(n)=(n3) 即算法的执行时间与问题规模n的三次方同阶。 一般情况下,随着n的增大,T(n)增长较慢的算法为最优算法。 (2)算法的空间复杂度 算法的空间复杂度是指算法执行时存储空间需求的度量。 记作S(n)=(f(n)) 通常只要分析算法在实现时所需的辅助空间单元个数即可。 算法的时间的耗费和所占存储空间的耗费是矛盾的,难以兼得。 一般情况,常常以算法的执行时间作为算法优劣的主要衡量指标。 13.2线性表 13.2.1线牲表的定义 线性表(Linear_List)是最常用且最简单的一种数据结构。 1.线性表的定义 线性表是由n(n>=0)个数据元素构成的有限序列,如(al,a2,...,ai,...,an),表中元素的个数n(n≥0)称为线性表的长度,n=0时,称为空表。 线性表中数据元素可以有不同的含义。 例如26个英文字母表: (A,B,C,···,Z)就是一个简单的线性表,表中每一个字母就是一个数据元素,每个元素之间存在着惟一的顺序关系。 在较为复杂的线性表中,数据元素可由若干数据项组成。 如学生成绩表中,每名学生的成绩信息由学号、数学成绩、物理成绩等数据项组成,一条信息称为一个记录,由这些记录组成的线性表称为文件。 2.线性表的特点 ⏹有且仅有一个被称作“第一个”的数据元素; ⏹有且仅有一个被称作“最后一个”的数据元素; ⏹除第一个和最后一个元素外,集合中的其他数据元素均只有一个前驱和一个后继。 对线性表进行的操作主要有如下几种: (1)初始化设定一个空的线性表。 (2)求长度即得到表中有多少个元素。 (3)取元素即得到表中某位置的元素。 (4〕定位给定值x,确定表中与之相等的元素的位置,如没有相等的元素存在,则给出错误信息。 (5)前插在指定位置的元素之前插入一个元素,插入后表中长度加1。 (6)删除删除指定位置的元素,删除后表中长度减1。 13.2.2线性表的存储及运算 1.线性表的顺序存储结构 线性表的顺序存储是一种最简单的存储方法,也称为顺序表。 可以用数组表示。 线性表的顺序存储具有以下特点: ⏹把线性表中数据元素依次存放到一组连续的存储单元中,每个数据元素对应一个存储单元,逻辑上相邻的元素存储在计算机前后相邻的存储空间中。 ⏹线性表中数据元素类型一致,占用相同大小的存储单元。 ⏹存储空间利用率高。 做插入、删除时需移动大量元素。 ⏹通过下标地址计算公式可容易得到第i个元素的存储地址,可随机存取表中任何一个元素,适合频繁查找操作的线性表。 2.线性表的链式存储结构 线性表的链式存储结构是指每个元素对应内存的一个存储空间,称作结点。 把线性表的元素存放到一个由这种结点组成的链表中,链表中的每个结点至少由两部分组成,即数据域和指针域。 链表结点结构如图13-3所示。 图13-3链表存储结点示意图 数据域存放线性表的一个元素,指针域存放其后继结点的地址,最后一个结点的指针域为空指针。 采用这种存储结构,逻辑上相邻的数据元素在内存中的物理存储空间不一定相邻。 例如线性表{元素1,元素2,元素3,元素4},按链式存储则对应的存储空间可能为图13-4所示。 图13-4链式存储结构示意图 具有链式存储结构的线性表也被称为线性链表或单链表。 链表可以有不带头结点和带头结点两种。 带头结点的线性链表的逻辑状态如图13-5所示。 头结点的数据域可以不存放任何信息,也可以存储线性表的长度等附加信息。 头指针变量L指向链表的头结点。 L a1 a2 an 图13-5带头结点的线性链表 不带头结点的线性链表逻辑状态如图13-6所示。 头指针变量L指向链表中的第一个结点。 L a1 a2 an 图13-6不带头结点的线性链表 3.链表结点的定义和分配 线性链表的数据结构可由C语言中的“结构类型”来描述。 typedefstructLNode { intdata;/*数据域*/ structLNodelink;/*指针域*/ }NODE; 线性表的存储空间可在程序运行期间动态分配,如需要向线性表中插入一个结点,只需调用c语言的动态空间分配函数(alloc或malloc函数)动态申请一个存储结点存放相应信息,并把新申请的结点插入到链表的适当位置上。 删除一个结点意味着结点将被系统回收。 C语言中free函数用来动态回收结点。 链式存储结构具有如下特点: (1)插入、删除运算灵活方便,不需要移动结点,只要改变结点中指针域的值即可。 (2)可以实现动态分配和扩展空间,当表中数据是动态产生的时候,最好使用链表。 (3)查找操作只能从链表的头结点开始顺序查找,不适合有大量频繁查找操作的表。 4.线性链表的基本运算 (1)建立链表 建立链表的程序采用动态申请结点的方法,从键盘上输入结点的数据。 下面程序段完成建立学生成绩链表,当学号输入为零时将结束链表的建立。 #include“stdio.h” #include“malloc.h” typedefstructstudent {intnum; floatscore; structstudent*link; }stu; stu*creat() {stu*head; stu*p1,*p2; intn=0; p1=p2=(stu*)malloc(sizeof(stu)); printf("inputnumberandscore: "); scanf("%d,%f",&p1->num1,&p1->score); head=NULL; while(p1->num! =0) {n=n+1; if(n==1)head=p1; elsep2->link=p1; p2=p1; p1=(stu*)malloc(sizeof(stu)); scanf("%d,%f",&p1->num,&p1->score); } p2->link=NULL; return(head); } (2)线性链表的插入操作 假设要在单链表中的两个数据元素a和b之间插入一个数据元素x,首先将指针P指向结点a,然后生成一个新结点,使其数据域为x,并使结点a中的link域指向新结点x,x的link域则指向结点b,即完成插入操作。 插入结点时指针变化状况如图13-7所示。 (a)插入前(b)插入后 图13-7在单链表中插入结点时指针变化状况 插入操作的算法描述: voidListInsert_L(NODE*L,inti,intx) {/*在带头结点的线性链表L中第i个位置之前插入元素x*/ NODE*p=L; intj=0; while(p&&j {p=p->link;++j};/*寻找第i-1个结点*/ if(! p||j>i-1)return(0);/*插入位置错误*/ s=(NODE*)malloc(sizeof(NODE));/*生成新结点*/ s->data=x;s->link=p->link;p->link=s;/*插入到表中*/ return (1); } } (3)线性链表的删除操作 在如图13-8所示的单链表中删除中间结点b的操作,只需将结点a的link域指向结点c,并释放b结点占用的存储空间即可。 图13-8在单链表中删除结点时指针变化状况 单链表的删除操作的算法描述: voidListDelete(NODE*L,inti,intx){ /*在带头结点的线性链表L中,删除第i个元素*/ NODE*p=L,q; intj=0; while(p->link&&j {p=p->link;++j};/*寻找第i个结点,并令p指向其前驱*/ if(! (p->link)||j>i-1)return(0);/*删除位置错误*/ q=p->link;p->link=q->link;free(q);/*删除并释放结点*/ return (1); } (4)线性链表的查找操作 设无表头结点的线性链表头指针为h,沿着链表的开始往后查找结点x,若找到,则返回该结点在链表中的位置,否则返回空地址。 单链表的查找操作的算法描述: NODE*lbcz(NODE*h,intx) {NODE*p; p=h;/*p先指向第一个结点*/ while(p! =NULL&&p->data! =x) p=p->link;/*p指向下一结点*/ return(p); } (5)循环链表 单链表中最后一个结点的指针域为空,如果将这个空指针改为指向头结点,整个链表就形成一个环。 这种首尾相接的链表称为循环链表。 在循环链表中,从任一结点出发均可找到表中其它结点。 循环链表的操作和线性链表基本一致。 图13-9所示为一个单循环链表。 图13-9循环单链表 13.3栈和队列 栈和队列是两种特殊的线性表,它们的运算要受到某些限制。 栈和队列广泛应用在计算机操作系统、编译原理和各种程序设计问题中。 13.3.1栈 1.栈的定义 栈(Stack)是限定只能在表的一端进行插入和删除的线性表,允许插入和删除的一端称为“栈顶”(top),不允许插入和删除的一端为封闭的,称为“栈底”(bottom)。 正因为只能在一端进行,因此后进入的元素只能先退出,所以也叫做“后进先出”(LastInFirstOut-LIFO)表,或“先进后出”表。 设栈s=(a1,a2,a3,…an),其中a1是栈底元素,an为栈顶元素,栈的原理示意图如图13-10所示。 图13-10栈的示意图 2.栈的存储结构及运算 (1)栈的存储结构 栈的存储结构也有顺序存储结构和链式存储结构两种表示方法。 用顺序存储结构表示的栈称为顺序栈。 顺序栈利用一组连续的存储单元存放自栈底到栈顶的数据元素;链栈是采用链表的形式存储数据元素。 无论是采用何种存储形式,对栈的存取原则都应遵循“后进先出”的原则。 (2)栈的运算 通常对栈进行的运算有: 设置一个空栈、插入一个新的栈顶元素(进栈)、删除栈顶元素(退栈)、读取栈顶元素。 创建一个栈要做的工作有两项: 一是确定栈的存贮结构,例如顺序栈可用数组来实现。 二是设置用于指示栈顶位置的栈指针top。 初始状态top=-1,表示栈空。 ①入栈运算 入栈运算是指在栈顶位置插入一个新元素,入栈运算的条件是栈未满。 当top=MAX-1(MAX为栈的最大存储空间),表示栈满,称为“上溢”,此时不能再进行入栈操作。 否则执行top=top+1(即栈顶指针加1),将新元素插入到栈顶位置。 2退栈运算 退栈运算是指取出栈顶的元素并赋给一个变量,退栈运算的条件是栈未空。 当top=-1,表示栈空,不能进行退栈操作。 若还要退栈,栈将溢出,称为“下溢”。 否则将栈顶元素赋给一个变量,然后执行top=top-1。 读栈顶元素运算是指取出栈顶的元素并赋给一个变量,但是不删除栈顶元素。 即栈顶指针不发生变化。 同样,当top=-1,表示栈空,不能进行读栈操作。 [例13-3]设数组s是一个顺序栈,栈的最大容量stacksize=4,初始状态top=-1。 图13-11给出栈的进栈和退栈运算的变化图示。 s[4]s[4] 10 top=-110进栈 栈空top=top+1;s[top]=10; 30 20 10 40 30 20 10 下面分别给出在栈s中实现进栈和退栈的算法。 进栈算法: #defineMAX100 intpush(ints[],intx,int*ptop) { inttop; top=*ptop; if(top==MAX-1) {printf(“overflow”);return(0);} else{*ptop=++top;/*实际栈顶指针加1*/ s[top]=x; return (1);} } 退栈算法: intpop(ints[],int*ptop,int*py) { inttop; top=*ptop; if(top==-1) {printf(“stackempty”);return(0);} else{*py=s[top];/*返回栈顶元素*/ --top; *ptop=top; return (1); } } 由于栈的后进先出的运算原则,常常被用于“回溯”求解的一类问题中,即某些问题的求解过程是采用试探方法,当某一路径受阻时,需要逆序退回,重新选择新路径,这样必须用栈记下曾经到达的每一状态,栈顶状态即是回退的第一站。 栈在计算机中的主要应用有过程的嵌套和递归调用、检验表达式括号是否匹配、表达式求值等。 13.3.2队列 1.队列的定义 队列(Queue)是只允许在表的一端进行插入,在表的另一端进行删除的线性表。 这如同生活中排队购物一样,从一端进队,从另一端出队,体现了“先来先服务”的原则。 所以这种结构称为先进先出(FirstInFirstOut-FIFO)表。 设队列q=(a1,a2,a3,…,an),其中a1是允许删除的一端,称为队首(front),an是允许插入的一端称为队尾(rear),队列示意图如13-12所示。 图13-12队列示意图 队列的主要运算有: 设置一个空队列;插入一个新的队尾元素(入队);删除队头元素(出队);读取队头元素。 2.队列的存储结构及与运算 (1)顺序存储结构 队列的顺序结构通常用高级语言中的一维数组实现。 在队的两端设置两个指针,其中front为头指针,rear为尾指针。 在初始化建空队列时,令front=rear=-1,当有新元素入队时,若队不满,尾指针加1,当有元素出队时,若队非空,头指针加1。 因此,在非空队列中,头指针始终指向队头元素的前一个位置,而尾指针始终指向队尾元素的位置(如图13-13)。 3 rear e4 2 rear e3 e3 1 e2 front 0 e1 rear=front=-1front (a)(b)(c) (a)空队列(b)e1,e2,e3入队(c)e1,e2出队,e4入队 图13-13队列的顺序存储结构 假设当前为队列分配的最大空间MAX为4,当队列处于图13-13(c)所示的状态时,即rear=MAX-1时,队满,此时不能继续向队列中插入新元素了,但实际上队列可用空间并没有满,解决这个问题的办法是,将队列头尾相连形成一个环,这种头尾相接的队列称为循环队列(如图13-14所示)。 front 图13-14循环队列中入队和出队时的指针变换过程 在图13-14所示的循环队列中,e3为队头元素,e4为队尾元素,之后e5,e6进入队列,此时循环队列全部占满,且有rear=front,与循环队列为空的条件相同,如图13-14(c)所示。 因此,在实际应用中为区别队空还是队满,通常采用的方法是,如果队尾指针加1与最大值取余数等于队头指针即为队满,这样在队满时实际队列中只有MAX-1个元素,即少用了一个元素空间。 如图13-14(b)所示。 (2)循环队列中加入和退出一个元素的算法: ①循环队列中加入一个元素的算法 intEnQueue(intQ[],intx,int*pf,int*pr) {intfront,rear; front=*pf;rear=*pr; if((rear+1)%MAX==front)/*循环队列判别队满的关系式*/ return(0); else {rear=(rear+1)%MAX; Q[rear]=x; *pr=rear; return (1);} } ②循环队列中删除一个元素的算法 intDeQueue(intQ[],int*py,int*pf,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 基础
