《数据结构实用教程(C语言版).ppt》由会员分享,可在线阅读,更多相关《数据结构实用教程(C语言版).ppt(608页珍藏版)》请在三一办公上搜索。
1、,数据结构实用教程(C语言版),第1章 概论,本章主要介绍以下内容:数据结构中涉及的相关概念数据结构研究的主要内容算法的概念、描述方法以及评价标准,本章目录,结束,1.1 什么是数据结构,1.1.1 基本概念及术语1.1.2 数据的逻辑结构1.1.3 数据的存储结构1.1.4 抽象数据类型,返回到本节目录,返回到总目录,1.1.1 基本概念及术语,在系统的学习数据结构知识之前,先了解一些相关概念和术语。1数据(Data)指所有能输入到计算机中并被计算机程序处理的符号的总称。例如,整数、实数、字符、图像、声音等都是数据。2数据元素(Data Element)数据元素(也称为结点)是数据的基本单位
2、,在计算机程序中通常作为一个整体进行考虑和处理。一个数据元素可以由若干个数据项组成。数据项是数据处理中不可分割的最小单位。,返回到本节目录,3数据结构(Data Structure)是相互之间存在一种或多种特定关系的数据元素的集合。这些数据元素不是孤立存在的,而是有着某种关系,这种关系称为结构。数据结构一般包括以下三个方面内容:(1)数据元素之间的逻辑关系,也称数据的逻辑结构。(2)数据元素及其关系在计算机存储器内的表示,称为数据的存储结构。(3)数据的运算,即对数据施加的操作。,返回到本节目录,1.1.1 基本概念及术语,数据结构定义:按某种逻辑关系组织起来的一批数据,按一定的映像方式把它存
3、放在计算机存储器中,并在这些数据上定义了一个运算的集合,就叫做数据结构。简言之,数据结构=逻辑结构+存储结构+运算集合。4数据类型(Data Type)数据类型是一组性质相同的值集合以及定义在这个值集合上的一组操作的总称。如在高级语言中,整型类型的取值范围为:-32768+32767,运算符集合为加、减、乘、除、取模,即+、-、*、/、%。,返回到本节目录,1.1.1 基本概念及术语,5数据类型(Data Type)高级语言中的数据类型分为两大类:(1)原子类型其值是不可分解的。如C语言中的标准类型(整型、实型、字符型)。(2)结构类型其值是由若干成分按某种结构组成的,因此是可以分解的。如C语
4、言中的的构造类型(结构体、共用体、枚举等类型)。,返回到本节目录,1.1.1 基本概念及术语,1.1.2 数据的逻辑结构,1定义数据的逻辑结构是指数据元素之间逻辑关系描述。可以用一个二元组表示,其形式化描述为:Data_Structure=(D,R)其中D是数据元素的有限集合,R是D上关系的有限集合。数据的逻辑结构是从逻辑关系上描述数据,与数据的存储无关,是独立于计算机的。,返回到本节目录,2数据的逻辑结构的分类根据数据元素之间的逻辑关系的不同特性,分为下列四类基本结构,如图1-1所示。(a)集合结构(b)线性结构(c)树型结构(d)图形结构图1-1 数据结构的四种基本逻辑结构,返回到本节目录
5、,1.1.2 数据的逻辑结构,(1)集合结构中的数据元素之间除了“同属于一个集合”的关系外,别无其他关系,这是一种最简单的数据结构。(2)线性结构结构中的数据元素之间存在着“一对一”的关系。【例1.1】学籍档案管理假设一个学籍档案管理系统应包含如表1-1所示的学生信息。,返回到本节目录,1.1.2 数据的逻辑结构,特点:表中的每一行是一个数据元素(或记录、结点),它由学号、姓名、性别及出生年月等数据项组成。表中数据元素之间是一种先后关系,对于表中任一结点,与它相邻且在它前面的结点(称为直接前驱)最多只有一个;与表中任一结点相邻且在其后的结点(称为直接后继)也最多只有一个。我们将这种关系称为“线
6、性结构”。,返回到本节目录,1.1.2 数据的逻辑结构,(3)树型结构结构中的数据元素之间存在着“一对多”的关系。【例1.2】人机对弈人与计算机进行对弈的部分图如图1-2为所示。图1-2 人机对弈图,返回到本节目录,1.1.2 数据的逻辑结构,特点:图中将每一个棋盘看作一个数据元素,则数据元素之间的关系要比表1-1要复杂许多。图中数据元素之间是一对多关系,即一个数据元素向上和一个数据元素相连(称为双亲结点),向下和多个数据元素相连(称为孩子结点)。我们将这种关系称为“树型结构”。4)图形结构或网状结构结构中的任意数据元素之间都可以有关系,元素之间存在着“多对多”的关系。,返回到本节目录,1.1
7、.2 数据的逻辑结构,(【例1.3】制定教学计划在制定教学计划时,需要考虑各门课程的开设顺序。有些课程需要先导先修课程,有些课程则不需要,而有些课程又是其他课程的先导先修课程。比如,计算机专业课程的开设情况如表1-2所示。,返回到本节目录,1.1.2 数据的逻辑结构,教学计划的关系图如图1-3所示。图1-3 教学计划关系图特点:图中数据元素存在着多对多的任意关系。一个结点可能有多个直接前驱和直接后继。,返回到本节目录,1.1.2 数据的逻辑结构,1.1.3 数据的存储结构,数据在计算机中的存储表示称为数据的存储结构,也称为物理结构。数据的存储结构是逻辑结构在计算机存储器中的实现。本书将介绍常用
8、的两种基本的存储结构:顺序存储结构和链式存储结构。数据的逻辑结构和存储结构的关系是:存储结构是逻辑关系的映像与元素本身映像,是数据结构的实现;逻辑结构是数据结构的抽象。,返回到本节目录,1.顺序存储结构顺序存储结构:借助元素在存储器中的相对位置来表示数据元素间的逻辑关系。【例1.4】对于表1-1提出的学生信息登记表进行存储,假定每个元素占用50个存储单元,数据从1000号单元开始由低地址向高地址存放,对应的顺序存储结构如表1-3所示。,返回到本节目录,1.1.3 数据的存储结构,顺序存储结构的主要特点:可实现对各数据元素的随机访问。这是因为只要知道存储的首地址以及每个数据元素所占的存储单元,就
9、可以计算出各数据元素的存储地址。不利于修改,在对数据元素进行插入、删除运算时可能要移动一系列的数据元素。,返回到本节目录,1.1.3 数据的存储结构,2.链式存储结构链式存储结构:借助指示元素存储地址的指针表示数据元素间的逻辑关系。【例1.5】对于表1-1学生信息登记表进行链式存储时,在每个数据元素后方附加一个指向“下一个结点地址”的指针字段,用于存放后继数据元素的存储地址,每个数据元素的地址是随机的,可以不连续。对应的链式存储结构见表1-4所示。,返回到本节目录,1.1.3 数据的存储结构,链式存储结构的主要特点:利于修改,在对数据元素进行插入、删除运算时,仅需修改数据元素的指针字段值,而不
10、必移动数据元素。由于逻辑上相邻的数据元素在存储位置中不一定相邻,因此,链式存储结构不能对数据元素进行随机访问。,返回到本节目录,1.1.3 数据的存储结构,1.1.4 抽象数据类型,1抽象数据类型的定义抽象数据类型(Abstract Data Type,简称ADT)是指一个数学模型以及定义在该模型上的一组操作。2抽象数据类型的表示抽象数据类型实际上就是对该数据结构的定义。因为它定义了一个数据的逻辑结构以及在此结构上的一组算法。可以用一个三元组表示:ADT=(,)其中,D是数据对象,S是D上的关系集,P是对D的基本操作集。,返回到本节目录,抽象数据类型通常是指由用户定义,用以表示应用问题的数据类
11、型,抽象数据类型由基本的数据类型组成,并包括一组相关的服务(或称操作)。抽象数据类型有些类似于pascal语言中的记录(record)类型和c语言中的构造(struct)类型,但它增加了相关的服务。3ADT的两个重要特征(1)数据抽象用ADT描述程序处理的实体时,强调的是其本质的特征、其所能完成的功能以及它和外部用户的接口(即外界使用它的方法)。(2)数据封装将实体的外部特性和其内部实现细节分离,并且对外部用户隐藏其内部实现细节。,返回到本节目录,1.1.4 抽象数据类型,1.2 算法和算法分析,1.2.1 算法的概念1.2.2 算法分析1.2.3 相关C语言知识回顾,返回到总目录,1.2.1
12、 算法的概念,1算法的定义瑞士著名的计算机科学家N.Wirth所提出的著名公式“程序=算法+数据结构”,所谓算法,就是为解决特定问题而采取的步骤和方法。2算法的特性一个算法应该具有下列特性:(1)有穷性:一个算法必须(对任何合法的输入值)在执行有限步之后结束。(2)确定性:算法中的每一条指令必须有确切的含义,不会产生二义性。,返回到本节目录,(3)可行性:算法中描述的操作都可以通过执行有限次基本操作来实现。(4)输入:一个算法有零个或多个输入。(5)输出:一个算法必有一个或多个输出。3算法的评价要设计一个好的算法通常需要考虑以下几方面的要求:(1)正确性:要求算法能够正确地执行预先规定的功能,
13、并达到所期望的性能要求。(2)可读性:为了便于理解、测试和修改算法,算法应该具有良好的可读性。,返回到本节目录,1.2.1 算法的概念,(3)健壮性:当输入非法的数据时,算法应能恰当地做出反应或进行相应处理,而不是产生莫名奇妙的输出结果。并且处理出错的方法不应是中断程序的执行,而是返回一个表示错误或错误性质的值,以便在更高的抽象层次上进行处理。(4)高效性:对同一个问题,执行时间越短,算法的效率越高。(5)低存储量:完成相同的功能,执行算法所占用的存储空间应尽可能的少。,返回到本节目录,1.2.1 算法的概念,4算法的描述为了表示一个算法,可以用多种不同的方法,常用的有自然语言、传统流程图、结
14、构化流程图、N-S流程图等表示。本书采用C的描述语言实现对各种数据结构及算法的操作描述,算法是以函数形式描述,描述如下:类型标识符 函数名(形式参数表)/*算法说明*/语句序列,返回到本节目录,1.2.1 算法的概念,1.2.2 算法分析,在算法满足正确性的前提下,如何评价不同算法的优劣呢?通常主要考虑算法的时间复杂度和空间复杂度这两方面。一般情况下,鉴于运算空间(内存)较为充足,所以把算法的时间复杂度作为重点分析。1.时间复杂度(Time Complexity)一个算法所需的运算时间通常与所解决问题的规模大小有关。问题规模是一个和输入有关的量,用n 表示问题规模的量,把算法运行所需的时间T表
15、示为n的函数,记为T(n)。不同的T(n)算法,当n增长时,运算时间增长的快慢很不相同。一个算法所需的执行时间就是该算法中所有语句执行次数之和。当n逐渐增大时T(n)的极限情况,一般简称为时间复杂度。,返回到本节目录,当讨论一个程序的运行时间时,注重的不是T(n)的具体值,而是它的增长率。T(n)的增长率与算法中数据的输入规模紧密相关,而数据输入规模往往用算法中的某个变量的函数来表示,通常是f(n)。随着数据输入规模的增大,f(n)的增长率与T(n)的增长率相近,因此T(n)同f(n)在数量级上是一致的。记作:T(n)=O(f(n)其中,大写字母O为Order(数量级)的字头,f(n)为函数形
16、式,如T(n)=O(n2)。,返回到本节目录,1.2.2 算法分析,注意,当T(n)为多项式时,可只取其最高次幂项并省略其系数,其它的次幂项及系数均略去不写。一般地,对于足够大的n,常用的时间复杂性存在以下顺序:O(1)O(log2n)O(n)O(nlog2n)O(n2)O(n3)O(2n)算法时间复杂度的数量级越大,表示该算法的效率越低,反之越高。例如 O(1)为常数数量级,,即算法的时间复杂性与输入规模n无关。,返回到本节目录,1.2.2 算法分析,【例1.6】分析以下算法的时间复杂度。x=0;y=0;for(k=1;k=n;k+)x+;(1)执行n次for(i=1;i=n;i+)for(
17、j=1;j=n;j+)y+;(2)执行n2次 解:T(n)=n+n2T(n)=O(n2)上述算法中的基本运算是语句(2),其执行频率为n2。则T(n)=n2=O(n2),返回到本节目录,1.2.2 算法分析,【例1.7】分析以下算法的时间复杂度。i=1;while(i=n)i=2*i;(1)执行f(n)次解:设语句(1)执行次数是f(n),则2f(n)n得到T(n)=O(log2n),返回到本节目录,1.2.2 算法分析,【例1.8】求两个矩阵相乘的函数的时间复杂度。void mult(int a,int b,int c)/*以二维数组存储矩阵元素,c为a和b的乘积*/for(i=1;i=n;
18、+i)(1)执行n次 for(j=1;j=n;+j)(2)执行n2次 ci,j=0;for(k=1;k=n;+k)(3)执行n3次 ci,j+=ai,k*bk,j;解:嵌套循环为每层循环次数的乘积,因为是该函数为三重循环,所以时间复杂度为O(n3)。,返回到本节目录,1.2.2 算法分析,2.空间复杂度(Space Complexity)一个算法的空间复杂度是指程序运行开始到结束所需要的存储空间。包括算法本身所占用的存储空间、输入/输出数据占用的存储空间以及算法在运行过程中的工作单元和实现算法所需辅助空间。类似于算法的时间复杂度,算法所需存储空间的量度记作:S(n)=O(f(n)表示随着问题规
19、模n的增大,算法运行所需存储量的增长率与f(n)的增长率相同。,返回到本节目录,1.2.2 算法分析,1.2.3 相关C语言知识回顾,1数据类型数据类型是和数据结构密切相关的一个概念,它最早出现在高级程序设计语言中,用以描述操作对象的特性。高级语言中的数据类型可分为两类:一类是原子类型,如C语言中的基本类型(整型、实型、字符型等)指针和空类型等不可再分的值;另一类是结构类型,如数组、结构体等通过若干成分(可以是原子类型或结构类型)构造而成的。,返回到本节目录,1.2.3 相关C语言知识回顾,(1)数组类型数组中的每一个元素都属于同一个数据类型。数组有一维数组和多维数组,数组名标识一个数组,下标
20、指示一个数组元素在该数组中的顺序位置,下标从0n-1(n为数据元素个数)。例如,int a10;定义了包含10个整数的数组a,数组下标范围09。(2)结构体类型结构体是是一种复合的数据类型,该结构体类型内可以有多个成员,每个结构体成员可以有不同的数据类型。基本格式如下:,返回到本节目录,【格式】struct 结构体名/*定义结构体类型*/成员列表;struct 结构体名 变量名表;/*定义结构体变量*/,返回到本节目录,1.2.3 相关C语言知识回顾,(3)结构体类型指针对结构体成员的引用方法当结构体类型的指针指向某一结构体变量时,就可以使用该指针对其指向的结构体变量内的各成员进行引用。引用的
21、方法为:指针名-成员名 等价于:(*指针名).成员名 等价于:结构体变量名.成员名在本书中,在程序内大量使用结构体类型的指针,所以大都采用第一种写法。,返回到本节目录,1.2.3 相关C语言知识回顾,(4)用typedef定义类型除了可以直接使用C提供的标准类型名和自己声明的结构体类型外,还可以用typedef声明新的类型名来代替已有的类型名。其基本定义格式如下:【格式】typedef 原类型名 新类型名;其中,typedef为关键字,表示重定义。原类型名是C语言提供的任意一种数据类型,可以是简单数据类型,也可以是构造数据类型。新类型名为原类型名的一个别名。使用新类型名就像使用原类型名那样定义
22、变量。,返回到本节目录,1.2.3 相关C语言知识回顾,2动态存储分配函数C语言提供了动态分配函数来实现动态存储分配。最常用的动态存储分配函数有以下两个。(1)分配内存空间函数malloc()【格式】(类型名*)malloc(要分配的内存字节数size)【功能】在内存中分配一个长度为size的连续存储空间,返回值是新分配存储空间的首地址,若内存不足,则返回NULL。其中,类型名表示把该存储空间用于何种数据类型。(类型名*)表示把malloc函数返回值强制转换为该类型指针。,返回到本节目录,1.2.3 相关C语言知识回顾,(2)释放内存空间函数free()【格式】free(指向要释放单元的指针名
23、)【功能】释放该指针所指的一块存储空间,该空间系统可另作它用。注意这个指针所指的空间必须是由malloc()函数和calloc()函数分配的才行。free函数无返回值。例如:int*pt;pt=(int*)malloc(sizeof(int);free(pt);程序段表示释放pt指向的存储空间。,返回到本节目录,1.2.3 相关C语言知识回顾,3函数的定义与调用在本书中,绝大多数的算法都是编写成C语言的函数,这些函数需要通过编写相应的主函数才能被执行。(1)函数的定义函数的定义如下:【格式】函数类型 函数名(形参表)内部变量定义 函数主体语句返回语句,返回到本节目录,1.2.3 相关C语言知识
24、回顾,(2)函数的调用【格式】函数类型 函数名(实参表);【说明】1实参表中各参数应与形参表中各参数类型及个数相符。2在调用函数时,表达式应与该函数的类型相符,如果该函数有返回值时,在调用时要将该函数的返回值赋给相同类型的变量。,返回到本节目录,1.2.3 相关C语言知识回顾,4 Turbo C 2.0中的汉字显示在TurboC 2.0中,如果想输入和显示汉字,需要使用UCDOS汉字系统。但现在的Windows操作系统不支持UCDOS汉字系统的16位显示。下面介绍一种能在Windows XP系统环境下,在TurboC 2.0中使用UCDOS的汉字系统的方法。,返回到本节目录,1.2.3 相关C
25、语言知识回顾,(1)将UCDOS的核心文件进行兼容性设置(有的机器可省略这步,直接执行(2)。点“开始”菜单-“所有程序”-“附件”-“程序兼容性向导”-“我想手动定位程序”-“浏览”-win98-256色,640X480-程序工作正确吗?选“是,设置此程序为一直使用兼容性设置。”完成。有的UCDOS版本的核心文件是knlvga.exe,也要照此进行兼容性设置。,返回到本节目录,1.2.3 相关C语言知识回顾,(2)运行UCDOS系统文件的方法。进入到命令提示符(MS-DOS状态)(“开始”-“程序”-“附件”-“命令提示符”)切换到UCDOS目录。(带下划线文字为输入的命令信息,“”表示回车
26、键。假设UCDOS文件夹在C盘根目录内,可输入如下命令:)C:Documents and Settingscd(回到C盘根目录下)C:cd Ucdos(进入UCDOS的目录),返回到本节目录,1.2.3 相关C语言知识回顾,这时不要运行UCDOS.BAT,分别一项一项命令运行。如:C:UDOSC:UDOSC:UDOSC:UDOSC:UDOS(五笔输入,如不用五笔可省略此步)(注:可直接输入命令名,如输入“rd16”,省略扩展名“.com”),返回到本节目录,1.2.3 相关C语言知识回顾,就可以顺利进入ucdos。然后,退出UCDOS目录,再进入tc目录运行tc.exe文件就可以在TurboC
27、 2.0中顺利的输入和显示汉字了。如:C:UDOScdC:cd tc(假定TurboC 2.0的文件夹名称为TC)C:TCtc.exe(可直接输入tc即可)即可进行TurboC 2.0输入并运行C语言源程序了。,返回到本节目录,1.2.3 相关C语言知识回顾,(3)常用的Turbo C2.0快捷键Alt+F:文件菜单Load 打开文件 Save 保存 Write to 另存为 Quit 退出Alt+R:运行菜单Run(或Ctrl+F9)运行程序 User Screen(或Alt+F5)查看运行结果Alt+E:编辑(显示光标,有时调试有错时可将光标重新显示在编辑区)F9:编译系统查错,返回到本节
28、目录,1.2.3 相关C语言知识回顾,(4)更改汉字输入法Alt+F1 区位Alt+F2 全拼Alt+F3 双拼Alt+F5 五笔(必须在UCDOS中输入wb命令才能用此项)Alt+F6 英文单次按右shift键 可显示或隐藏中文输入法,返回到本节目录,1.2.3 相关C语言知识回顾,1.3本章小结,本章主要介绍了有关数据结构的以下几方面:(1)数据结构主要研究数据的逻辑结构、存储结构和运算方法。(2)数据的逻辑结构包括:集合、线性结构、树型结构、图形结构四种基本类型。(3)数据的存储结构包括:顺序存储结构和链式存储结构。(4)算法是对特定问题求解步骤的一种描述,是指令的有限序列。算法具有:有
29、穷性、确定性、正确性、输入、输出等特性。(5)算法的时间复杂度与空间复杂度。,返回到总目录,数据结构实用教程(C语言版)中国水利水电出版社,第2章 线性表,本章主要介绍下列内容线性表的定义和基本操作线性表的顺序存储结构线性表的链式存储结构线性表的应用举例,本章目录,结束,2.1 线性表的基本概念,2.1.1 线性表的定义2.1.2 线性表的基本操作,返回到总目录,2.1.1 线性表的定义,1.线性表的定义线性表是具有相同数据类型的n(n=0)个数据元素的有限序列,通常记为:(a1,a2,ai-1,ai,ai+1,an)其中n为表长,当n=0时称为空表。在线性表中相邻元素之间存在着顺序关系。如对
30、于元素ai 而言,ai-1 称为 ai 的直接前驱,ai+1 称为 ai 的直接后继。,返回到本节目录,2.线性表的特点(1)有且仅有一个开始结点(a1),它没有直接前驱;(2)有且仅有一个终端结点(an),它没有直接后继;(3)除了开始结点和终端结点以外,其余的结点都有且仅有一个直接前驱和一个直接后继。,2.1.1 线性表的定义,返回到本节目录,2.1.2 线性表的基本操作,数据结构的运算是定义在逻辑结构层次上的,而运算的具体实现是建立在存储结构上的。下面定义的线性表的基本操作仅是定义在逻辑结构上的,每一个操作的具体实现只有在确定了线性表的存储结构之后才能完成。线性表的基本操作有:(1)初始
31、化线性表InitList(L)。其作用是建立一个空表L(即建立线性表的构架,但不包含任何数据元素)。,返回到本节目录,(2)求线性表的长度GetLength(L)。其作用是返回线性表L的长度(即所含数据元素的个数)。(3)求线性表中第i个元素GetElem(L,i,x)。其作用是在1iGetLength(L)返回成功,并用x存储线性表L的第i个元素的值。(4)按值查找操作Locate(L,x)。在线性表L查找一个与给定值x相等的数据元素,其作用是若存在一个或多个与x相等的数据元素,则返回的元素所在位置的最小值或地址值;否则返回0或NULL值。,2.1.2 线性表的基本操作,返回到本节目录,(5
32、)插入操作InsElem(L,i,x)。其作用是在线性表L的第i个位置上插入一个值为 x 的新元素,使线性表L由(a1,a2,ai-1,ai,ai+1,an)变为(a1,a2,ai-1,x,ai,ai+1,an)。其中1iGetLength(L)+1。(6)删除操作DelElem(L,i,x)。其作用是删除线性表L的第i个位置的数据元素并用x将其存储,使线性表L由(a1,a2,ai-1,ai,ai+1,an)变为(a1,a2,ai-1,ai+1,an)。其中1iGetLength(L)。(7)输出元素值DispList(L)。其作用是依次扫描线性表L,并输出各元素的值。,2.1.2 线性表的基
33、本操作,返回到本节目录,2.2 顺序表,2.2.1 顺序表2.2.2 顺序表的基本操作实现,返回到总目录,1.顺序表的定义数据结构在内存中的表示通常有两种形式,即顺序存储表示和链式存储表示。线性表的顺序存储表示又称为顺序表。线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表的数据元素,我们把用这种存储形式存储的线性表称为顺序表。假设顺序表(a1,a2,ai-1,ai,ai+1,an),每个数据元素占用d个存储单元,则元素ai的存储位置为:Loc(ai)=Loc(a1)+(i-1)d 1in,2.2.1 顺序表,返回到本节目录,其中,Loc(a1)是顺序表第一个元素a1的存储位置,通称为
34、顺序表的起始地址。顺序存储结构示意图如图2-1所示。顺序表的存储特点:(1)顺序表的逻辑顺序和物理顺序是一致的。(2)顺序表中任意一个数据元素都可以随机存取,所以顺序表是一种随机存取的存储结构。,2.2.1 顺序表,返回到本节目录,2.顺序表的类型定义#define MAXLEN 100/*定义常量MAXLEN为100表示存储空间总量*/#define OK/*定义常量OK为1表示成功*/#define ERROR 0/*定义常量ERROR为0表示失败*/#define OVER-1/*定义常量OVER为-1表示结束*/typedef int ElemType;/*定义ElemType为int
35、类型*/typedef struct/*顺序表存储类型*/ElemType dataMAXLEN;/*存放线性表的数组*/int Length;/*Length是顺序表的长度*/SeqList;,2.2.1 顺序表,返回到本节目录,2.2.2 顺序表的基本操作实现,1顺序表的初始化顺序表的初始化即构造一个空顺序表L,将表L的实际长度置0,算法描述见算法2.1。算法2.1void InitList(SeqList*L)L-Length=0;/*初始的化顺序表为空*/,返回到本节目录,2顺序表的建立初始化顺序表后向表中输入n个元素建立表L,算法描述见算法2.2。算法2.2void CreateLi
36、st(SeqList*L,int n)int i;printf(请输入各个元素值:n);for(i=0;idatai);L-Length=i;,2.2.2 顺序表的基本操作实现,返回到本节目录,3求顺序表的长度操作返回顺序表L的Length值,算法描述见算法2.3。算法2.3int GetLength(SeqList*L)return L-Length;4查找操作顺序表的查找分为按值与按序号查找,下面将分别介绍这两种方法的实现,读者可根据具体的问题相应选择所需的查找方法。,2.2.2 顺序表的基本操作实现,返回到本节目录,(1)按号查找查找顺序表中第i个元素的值,在i无效时返回出错,有效时返回
37、成功,并用x存储第i个元素的值,算法描述见算法2.4。算法2.4int GetElem(SeqList*L,int i,ElemType*x)if(iL-Length)return ERROR;else*x=L-datai-1;return OK;,2.2.2 顺序表的基本操作实现,返回到本节目录,2)按值查找操作顺序表中的按值查找是指在顺序表中查找与给定值x相等的数据元素的所在位置,算法描述见算法2.5。算法2.5int Locate(SeqList*L,ElemType x)int i=0;while(i Length/*返回的是元素位置*/,2.2.2 顺序表的基本操作实现,返回到本节目
38、录,2.2.2 顺序表的基本操作实现,5插入操作 线性表的插入是指在表的第i个位置上插入一个值为 x 的新元素,插入后使原表长增1,原顺序表如图2-2所示。,返回到本节目录,5插入操作,步骤如下:(1)将anai 之间的所有结点依次后移,为新元素让出第i个位置,如图2-3所示。,返回到本节目录,5插入操作,步骤如下:(2)将新结点x插入到第i个位置,如图2-4所示。(3)顺序表的长度增1,插入成功,并返回,算法描述见算法2.6。,返回到本节目录,5插入操作,算法2.6int InsElem(SeqList*L,int i,ElemType x)int j;if(L-Length=MAXLEN)
39、printf(顺序表已满!);return OVER;/*表满,不能插入*/if(iL-Length+1)/*检查给定的插入位置的正确性*/printf(插入位置出错!);return ERROR;if(i=L-Length+1)L-datai-1=x;L-Length+;return OK;/*插入的位置为表尾,则不需移动直接插入即可*/for(j=L-Length-1;j=i-1;j-)/*结点移动*/L-dataj+1=L-dataj;L-datai-1=x;/*新元素插入*/L-Length+;/*顺序表长度增1*/return OK;/*插入成功,返回*/,返回到本节目录,5插入操作
40、,插入算法的时间性能分析:顺序表插入操作大约需移动表中一半数据元素,其时间复杂度为(n)。,返回到本节目录,2.2.2 顺序表的基本操作实现,6.删除操作 线性表的删除操作是指将第 i 个元素从顺序表中去掉,删除后顺序表表长减1,原顺序表如图2-5所示。,返回到本节目录,6.删除操作,步骤如下:(1)将要删除的元素值赋给指针变量*x,如图2-6所示,返回到本节目录,6.删除操作,步骤如下:(2)将ai+1an 之间的结点依次顺序向前移动,如图2-7所示。(3)顺序表的长度减1,删除成功,并返回,算法描述见算法2.7。,返回到本节目录,6.删除操作,算法2.7int DelElem(SeqLis
41、t*L,int i,ElemType*x)int j;if(L-Length=0)printf(顺序表为空!);return ERROR;/*表空,不能删除*/if(iL-Length)/*检查是否空表及删除位置的合法性*/printf(不存在第i个元素);return ERROR;*x=L-datai-1;/*用指针变量*x返回删除的元素值*/for(j=i;jLength-1;j+)/*结点移动*/L-dataj-1=L-dataj;L-Length-;/*顺序表长度减1*/return OK;/*删除成功,返回*/,返回到本节目录,6.删除操作,删除算法的时间性能分析:与插入操作相同,其
42、时间主要消耗在了移动表中元素上,(大约需要移动表中一半的元素),显然该算法的时间复杂度为(n)。,返回到本节目录,2.2.2 顺序表的基本操作实现,7 顺序表的输出操作扫描顺序表L,输出各元素的值,算法描述见算法2.8。算法2.8void DispList(SeqList*L)int i;for(i=0;iLength;i+)printf(%5d,L-datai);,返回到本节目录,2.3 链表,2.3.1 单链表2.3.2 单链表的基本操作实现2.3.3 链表的变形,返回到总目录,2.3.1 单链表,1.单链表的定义线性表的链式存储结构是指用一组任意的存储单元(可以连续,也可以不连续)存储线
43、性表中的数据元素。为了反映数据元素之间的逻辑关系,对于每个数据元素不仅要表示它的具体内容,还要附加一个表示它的直接后继元素存储位置的信息,这样构成的链表称为线性单向链接表,简称单链表,其结点结构如图2-8所示。,图2-8 单链表的结点示意图,其中,data部分称为数据域,用于存储一个数据元素(结点Node)的信息。next部分称为指针域,用于存储其直接后继的存储地址的信息。,返回到本节目录,2.3.1 单链表,单链表分为带头结点(其next域指向链表第一个结点的存储地址)和不带头结点两种类型。在许多情况下,带头结点的链表中每个结点的存储地址均放在其前驱结点中,这样算法对所有的结点处理可一致化,
44、因此,本节讨论的单链表均指带头结点的单链表。带头结点的单链表如图2-9所示。,图2-9 带头结点的单链表,其中,头结点的数据域可以不存储任何信息,也可以存放特殊的信息;头结点的指针域存储链表中第一个结点的地址。当头结点的指针域为空(即NULL或用表示),则此表为空表。在非空表中,当某个结点的指针域为空,表示它为链表的最后一个结点。,返回到本节目录,2.3.1 单链表,链式存储特点:线性表的链式存储结构是通过指针来表示元素之间的逻辑关系,不再有逻辑顺序与物理存储顺序一致的特点,是非顺序存储结构。,返回到本节目录,2.3.1 单链表,2.单链表的类型定义#define OK 1#define ER
45、ROR 0#define OVER-1typedef char ElemType;/*定义ElemType为char类型*/typedef struct node/*单链表存储类型*/ElemType data;/*定义结点的数据域*/struct node*next;/*定义结点的指针域*/LinkList;,返回到本节目录,2.3.2 单链表的基本操作实现,1.单链表的初始化单链表的初始化即构造一个仅包含头结点的空单链表L,算法描述见算法2.9。算法2.9LinkList*InitList()/*申请一块LinkList类型的存储单元的操作,并将其地址赋值给头指针变量L*/LinkList
46、*L;L=(LinkList*)malloc(sizeof(LinkList);L-next=NULL;return L;/*头结点L指针域为空,表示空链表*/,返回到本节目录,2.3.2 单链表的基本操作实现,2单链表的建立(1)头插法建表在初始化链表后,每读取有效的数据都为其生成新结点s,并将读取的数据存放到新结点s的数据域中,然后将新结点插入到当前链表L的表头上,直到循环结束为止,算法描述见算法2.10。算法2.10void CreateList(LinkList*L)LinkList*s;char ch;while(ch=getchar()!=#)/*判断输入数据是否有效*/s=(Li
47、nkList*)malloc(sizeof(LinkList);/*生成新结点*/s-data=ch;/*将数据放入新结点的数据域*/s-next=L-next;/*将新结点的指针域存放头结点的指针域*/L-next=s;/*将新结点插入头结点之后*/,返回到本节目录,2.3.2 单链表的基本操作实现,(2)尾插法建表头插法建立链表虽然算法简单易理解,但生成的链表中结点的次序和原输入的次序相反。而尾插法建立链表可实现次序的一致,该算法依旧在初始化链表后,但需增加一个尾指针last,使其指向当前链表的尾结点,算法描述见算法2.11。算法2.11void CreateList(LinkList*L
48、)LinkList*s,*last;char ch;last=L;/*last始终指向尾结点,开始时指向头结点*/while(ch=getchar()!=#)/*判断输入数据是否有效*/s=(LinkList*)malloc(sizeof(LinkList);/*生成新结点*/s-data=ch;/*将数据放入新结点的数据域*/s-next=NULL;/*将新结点的指针域为空*/last-next=s;/*将新结点插入表尾*/last=s;/*将last指针指向表尾结点*/,返回到本节目录,2.3.2 单链表的基本操作实现,3求单链表的长度求长度就是求单链表中数据元素的个数。求带头结点的单链表
49、L的长度需设一个动态指针p指向头结点,计数器j初始值为0,p指针所指结点后面若还有结点,p就向后移动,每次移动一次,计数器j值增1,直到p所指结点后面为空结束,则计数器j的值即为表的长度,算法描述见算法2.12。算法2.12int GetLength(LinkList*L)LinkList*p;int j=0;p=L;/*p指向链表的头结点*/while(p-next!=NULL)/*判断p所指结点后面是否为空*/p=p-next;/*p向所指结点的后面移动*/j+;/*计数器值增1*/return j;/*计数器j的值为表长度*/,返回到本节目录,2.3.2 单链表的基本操作实现,4查找操作
50、单链表的查找分为按值与按序号查找,下面将分别介绍这两种方法的实现,读者可根据具体的问题相应选择所需的查找方法。(1)按值查找算法思路:从链表的第一个元素结点开始,由前向后依次比较单链表中各结点数据域中的值,若某结点数据域中的值与给定的值x相等,则返回该结点的指针值p;否则继续向后比较直到表结束。若整个单链表中没有这样的结点,则返回空NULL值,算法描述见算法2.13。算法2.13LinkList*Locate(LinkList*L,ElemType x)LinkList*p;p=L-next;/*p指向链表的第一个结点*/while(p!=NULL,返回到本节目录,2.3.2 单链表的基本操作