1、第2章 线性表,2.1 线性表的逻辑结构 2.2 线性表的顺序表示和实现 2.3 线性表的链式表示和实现 2.4 应用举例,1,作业,上堂课要点回顾,2,线性结构(包括表、栈、队、数组)的定义和特点: 仅一个首、尾结点,其余元素仅一个直接前驱和一个直接后继。,2. 线性表,逻辑结构:“一对一” 或 1:1 存储结构:顺序、链式 运 算 :修改、插入、删除,3.顺序存储,特征:逻辑上相邻,物理上也相邻; 优点:随机查找快 O(1) 缺点:插入、删除慢 O(n),2.3 线性表的链式表示和实现,3,2.3.1 链表的表示 2.3.2 链表的实现 2.3.3 链表的运算效率分析,本节小结,作 业,(
2、续上堂课),2.3.1 链表的表示,链式存储特点 与链式存储有关的术语 补充:结构数据类型及其C语言表示法,4,5,1. 链式存储特点:逻辑上相邻,物理上不一定相邻,链表存放示意图如下:,讨论1 :每个存储结点都包含两部分:数据域和 。,讨论2:在单链表中,除了首元结点外,任一结点的存储位置由 指示。,其直接前驱结点的链域的值,指针域(链域),2. 与链式存储有关的术语,6,1)结点:数据元素的存储映像。由数据域和指针域两部分组成; 2)链表: n 个结点由指针链组成一个链表。它是线性表的链式存储映像,称为线性表的链式存储结构。 3)单链表、双链表、多链表、循环链表: 结点只有一个指针域的链表
3、,称为单链表或线性链表; 有两个指针域的链表,称为双链表; 有多个指针域的链表,称为多链表; 首尾相接的链表称为循环链表。,4)头指针、头结点和首元结点,以教材P27 图2.5和图2.6内容为例说明。,何谓头指针、头结点和首元结点?,7,头指针是指向链表中第一个结点(或为头结点或为首元结点)的指针。单链表可由一个头指针唯一确定。 头结点是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息; 首元结点是指链表中存储线性表第一个数据元素a1的结点。,例:,8,一个线性表的逻辑结构为:(ZHAO,QIAN,SUN,LI,ZHOU,WU,ZHENG,WANG),其存储结构用单链表表示
4、如下,请问其头指针的值是多少?,答:头指针是指向链表中第一个结点的指针,因此关键是要寻找第一个结点的地址。,31,头指针的值是31,9,上例链表的逻辑结构示意图有以下两种形式:,区别: 无头结点 有头结点,10,答:,讨论1. 在链表中设置头结点有什么好处?,讨论2. 如何表示空表?,头结点即在链表的首元结点之前附设的一个结点,该结点的数据域中不存储线性表的数据元素,其作用是为了对链表进行操作时,可以对空表、非空表的情况以及对首元结点进行统一处理,编程更方便。,答:,无头结点时,当头指针的值为空时表示空表; 有头结点时,当头结点的指针域为空时表示空表。,讨论3. 头结点的数据域内装的是什么?,
5、11,头结点的数据域可以为空,也可存放线性表长度等附加信息,但此结点不能计入链表长度值。,答:,讨论4. 链表的数据元素有两个域,不再是简单数据类型,编程时该如何表示?,因每个结点至少有两个分量,所以要采用结构数据类型。,答:,什么是结构类型?如何书写表达?补充C语言内容,头结点的数据域,3. 补充结构数据类型及其C语言表示法, 类型定义和变量说明可以合写为: typedef struct liuyu /liuyu是自定义结构类型名称 char data; /定义数据域的变量名及其类型 struct liuyu *next; /定义指针域的变量名及其类型 test,*head; /*test是
6、liuyu结构类型的变量, *head是liuyu结构类型的头指针*/,12,以26个字母的链表为例,每个结点都有两个分量:,假设每个结点用变量test表示, 其中两个分量分别用data和*next表示,则:,补充:结构类型的C语言表示法,13, 对于指向结构变量test首地址的指针,还可说明为: struct test *p, *q; (或用 struct liuyu *L;)/注:刚才已经定义了test为用户自定义的liuyu类型, 每个结点的分量如何表示?,方式1:直接用 test.dataa; test.next=q 方式2:先让指针变量p指向该结点的首地址,然后用: p-data=a
7、; p-next=q; 方式3:先让指针变量p指向该结点的首地址,然后用: (*p).data=a; (*p).nextq,至此应可看懂教材P28对于单链表的抽象描述,和 教材P22关于顺序表的抽象定义。,14,设p为指向链表的第i个元素的指针,则第i个元素的 数据域写为 ,指针域写为 。,练习:,p-data,ai的值,p-next,ai+1的地址,附2:教材P22关于顺序表的抽象定义:,15,Typedef struct /若后面不再用,可省略结构名ElemType *elem; /表基址int length; /表长(特指元素个数)int listsize; /表当前存储容量(字节数)
8、SqList;,Typedef struct Lnode ElemType data; /数据域struct Lnode *next; /指针域 *LinkList; / *LinkList为Lnode类型的指针,附1:教材P28对于单链表的抽象描述:,补充:结构类型的C语言表示法, 介绍三个有用的库函数(都在 中): sizeof(x)计算变量x的长度; malloc(m) 开辟m字节长度的地址空间,并返回这段空间的首地址; free(p) 释放指针p所指变量的存储空间,即彻底删除一个变量。,16,问1:自定义结构类型变量test的长度m是多少?,问2:结构变量test的首地址(指针p)是多
9、少?,问3:怎样删除结构变量test?只能借助其指针删除!,msizeof(test),p(test*)malloc(m),free(p),2.3.2 链表的实现,1. 单链表的建立和输出 2. 单链表的修改 3. 单链表的插入 4. 单链表的删除 5. 应用举例 6. 其它链表形式,17,1. 单链表的建立和输出,实例:用单链表结构来存放26个英文字母组成的线性表(a,b,c,z),请写出C语言程序。,18,难点分析:每个数据元素在内存中是“零散”存放的,其首地址怎么找?又怎么一一链接?,实现思路:先开辟头指针,然后陆续为每个数据元素开辟存储空间并赋值,并及时将地址送给前面的指针。,将全局变
10、量及函数提前说明:,#include #include typedef struct liuchar data; struct liu*next;test; liu *p,*q,*head; /一般需要3个指针变量 int n ; / 数据元素的个数 int m=sizeof(test); /*结构类型定义好之后,每个变量的长度就固定了,m求一次即可*/,19,void build() /字母链表的生成。要一个一个慢慢链入, int i; head=(test*)malloc(m); /m=sizeof(test) 前面已求出 p=head; for(i=1;idata=i+a-1; / 第一
11、个结点值为字符a p-next=(test*)malloc(m); /为后继结点开新空间! p=p-next; /让指针变量P改为指向后继结点 p-data=i+a-1; /最后一个元素要单独处理 p-next=NULL ; /单链表尾结点的指针域要置空!,20,新手特别容易忘记!,void display() /*字母链表的输出*/,p=head; while (p-next!=NULL) /* 只要没到最后一个元素,就不停地“顺藤摸瓜”输出*/ printf(“%c“,p-data); p=p-next; printf(“%cn”,p-data); /别忘记输出尾结点数据 ,21,讨论:要
12、统计链表中数据元素的个数,该如何改写?,sum +;,2. 单链表的修改(或读取),思路:要修改第i个数据元素,关键是要先找到该结点的指针p,然后用p-data=new_value 即可。,22,难点:单链表中想取得第i个元素,必须从头指针出发寻找(顺藤摸瓜),不能随机存取 。,核心语句:见教材P29的GetElem_L函数说明,Status GetElem_L(LinkList L, int i, ElemType ,3. 单链表的插入,23,在链表中插入一个元素的示意图如下:,插入步骤(即核心语句):,Step 1:s-next=p-next; Step 2:p-next=s ;,p-ne
13、xt,s-next,思考:步骤1和2能互换么?,元素x结点应预先生成: S=(test*)malloc(m); S-data=x; S-next=p-next,4. 单链表的删除,24,在链表中删除某元素的示意图如下:,删除步骤(即核心语句):,q = p-next; /保存b的指针,靠它才能指向c p-next=q-next; /a、c两结点相连 free(q) ; /删除b结点,彻底释放,p-next,思考: 省略free(q)语句行不行?,(p-next) - next,5.应用举例:两个链表的归并(教材P31),25,算法要求: 已知:线性表 A、B,分别由单链表 LA , LB 存储
14、,其中数据元素按值非递减有序排列, 要求:将 A ,B 归并为一个新的线性表C , C 的数据元素仍按值非递减排列 。设线性表 C 由单链表 LC 存储。 假设:A=(3,5,8,11),B=(2,6,8,9,11) 预测:合并后 C =( 2 , 3 , 5 , 6 , 8 , 8 , 9 , 11,11 ),用链表可表示为:,26,头结点,算法分析:,27,算法主要包括:搜索、比较、插入三个操作:搜索:需要两个指针搜索两个链表;比较:比较结点数据域中数据的大小;插入:将两个结点中数据小的结点插入新链表。,28,La,Pa、Pb用于搜索La和Lb, Pc指向新链表当前结点,29,算法实现:,
15、Void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc) /按值排序的单链表LA,LB,归并为LC后也按值排序,free(Lb); /释放Lb的头结点 /MergeList_L,pc-next = pa?pa:pb ; /插入剩余段,while(pa pb=pb-next ,pa=Lanext; pb=Lbnext; Lc=pc=La; /初始化,思考:,30,1、不用Lc,直接把La表插到Lb表中;或者把Lb表插到La表中,如何编程?,2、要求不能有重复的数据元素,如何编程?,6. 其它链表形式,31,答:能。只要定义一个结构类型(含数
16、据域和指示域)数组,就可以完全描述链表,这种链表称为静态链表。 注:数据域含义与前面相同,指示域相当于前面的指针域。,讨论1: 用一维数组也能存放链表吗?怎样实现?,静态链表的插入与删除操作与普通链表一样,不需要移动元素,只需修改指示器就可以了。,具体实现过程见教材P31-34。,讨论2: 链表能不能首尾相连?怎样实现?,32,答:能。只要将表中最后一个结点的指针域指向头结点即可 (P-next=head;) 。这种形成环路的链表称为循环链表。,特别:带头结点的空循环链表样式,参见教材P35,特点:1、从任一结点出发均可找到表中其他结点。2、操作仅有 一 点与单链表不同:循环条件单链表 - p = NULL 或 p -next =NULL循环链表- p= head 或 p-next = head,讨论3: 单链表只能查找结点的直接后继,能不能查找直接前驱?如何实现?,33,答:能。只要把单链表再多开一个指针域即可(例如用*next和*prior;) 。,双向链表在非线性结构(如树结构)中将大量使用。,这种有两个指针的链表称为双向链表。其特点是可以双向查找表中结点。参见教材P3539。,特别:带头结点的空双向链表样式:,作业:,1. 请在下次课前完成第2章自测卷全部内容;,34,2. 请试做教材配套题集的2.2、2.4、2.5、2.8、2.10和2.11。,