收藏 分享(赏)

第二章 线性表-2.ppt

上传人:gnk289057 文档编号:9241398 上传时间:2019-07-30 格式:PPT 页数:120 大小:2.54MB
下载 相关 举报
第二章 线性表-2.ppt_第1页
第1页 / 共120页
第二章 线性表-2.ppt_第2页
第2页 / 共120页
第二章 线性表-2.ppt_第3页
第3页 / 共120页
第二章 线性表-2.ppt_第4页
第4页 / 共120页
第二章 线性表-2.ppt_第5页
第5页 / 共120页
点击查看更多>>
资源描述

1、第二章 线性表,2,1.了解线性表的逻辑结构特性:数据元素之间存在着线性关系。 2.熟练掌握这两类存储结构:顺序存储结构和链式存储结构。链表是本章的重点和难点。扎实的指针操作和内存动态分配的编程技术是学好本章的基本要求。 3.熟练掌握线性表在顺序存储结构上实现基本操作:查找、插入和删除的算法。 4.熟练掌握不同链表结构中线性表操作的基本方法,能在实际应用中选用适当的链表结构。 5. 能够从时间和空间复杂度的角度综合比较线性表两种存储结构的不同特点及其适用场合。,学习提要,3,在数据元素的非空有限集中: 存在唯一的一个被称作“头元素”的数据元素; 存在唯一的一个被称作“尾元素”的数据元素; 除头

2、元素外,集合中的每个数据元素均只有一个前驱; 除尾元素外,集合中的每个数据元素均只有一个后继。,线性结构特点,a1,a2,a3,a4,a5,a6,简言之,线性结构反映结点间的逻辑关系是 1:1 的。,线性结构包括:线性表、堆栈、队列、字符串、数组等,其中最典型、最常用的是线性表,4,(a1, a2, ai-1,ai, ai1 ,, an),1. 线性表的定义:用数据元素的有限序列表示,n=0时称为,数据元素,线性起点,ai的直接前趋,ai的直接后继,下标,是元素的序号,表示元素在表中的位置,n为元素总个数,即表长,空表,线性终点,2.1 线性表的逻辑结构,5,例1、26个英文字母组成的字母表(

3、A,B,C、Z)例2、某校从1978年到1983年各种型号的计算机拥有量的变化情况。(6,17,28,50,92,188),6,例3、学生健康情况登记表如下:,a1 a2 a3 a4 .,7,ADT List 数据对象:Dai|aiElemSet, i=1,2,.,n, n0 数据关系:R|ai-1 ,aiD,i=2,.,n 基本操作: (1)结构初始化:InitList( &L ) 操作结果:构造一个空的线性表L。 (2)销毁结构:DestroyList( &L ) 初始条件:线性表L已存在。 操作结果:销毁线性表L。,线性表的抽象数据类型,8,(3)引用型操作: ListEmpty( L

4、) 初始条件:线性表L已存在。 操作结果:若L为空表,则返回TRUE,否则FALSE ListLength( L ) 初始条件:线性表L已存在。 操作结果:返回L中元素个数。 PriorElem( L, cur_e, &pre_e ) 初始条件:线性表L已存在。 操作结果:若cur_e是L的元素,但不是第一个,则用pre_e 返回它的前驱,否则操作失败,pre_e无定义。 NextElem( L, cur_e, &next_e ) 初始条件:线性表L已存在。 操作结果:若cur_e是L的元素,但不是最后一个,则用 next_e返回它的后继,否则操作失败,next_e无定义。,9,(3)引用型操

5、作: (续) GetElem( L, i,&e) 初始条件:线性表L已存在, 1iLengthList(L) 操作结果:用e返回L中第i个元素的值。 LocateElem( L, e, compare( ) ) 初始条件:线性表L已存在,compare( )是元素判定函数。 操作结果:返回L中第1个与e满足关系compare( )的元素 的位序。若这样的元素不存在,则返回值为0。 ListTraverse(L, visit( ) /线性表遍历 初始条件:线性表L已存在。 操作结果:依次对L的每个元素调用函数visit( )。一旦visit( )失败,则操作失败。,10,(4) 加工型操作 Cl

6、earList( &L ) 初始条件:线性表L已存在。 操作结果:将L重置为空表。 PutElem( &L, i, e ) 初始条件:线性表L已存在,1iLengthList(L) 操作结果:L中第i个元素赋值同e的值。 ListInsert( &L, i, e ) 初始条件:线性表L已存在,1iLengthList(L)+1 操作结果:在L的第i个元素前插入新元素e,L长度增1。 ListDelete(&L, i, &e) 初始条件:线性表L已存在且非空,1iLengthList(L) 操作结果:删除L第i个元素,用e返回其值,L的长度减1。 ADT List,11,某数据结构上的基本运算,

7、不是它的全部运算,而是一些常用的基本的运算,而每一个基本运算在实现时也可能根据不同的存储结构派生出一系列相关的运算来。掌握了某一数据结构上的基本运算后,其它的运算可以通过基本运算来实现,也可以直接去实现。 2. 在上面各操作中定义的线性表仅仅是一个抽象在逻辑结构层次的线性表,尚未涉及到它的存储结构,因此每个操作在逻辑结构层次上尚不能用具体的某种程序语言写出具体的算法,而算法的实现只有在存储结构确立之后。,说明,12,假设:有两个集合 A 和 B 分别用两个线性表 LA 和 LB 表示,即:线性表中的数据元素即为集合中的成员。现要求一个新的集合AAB。,例 2-1,13,要求对线性表作如下操作:

8、 扩大线性表 LA,将存在于线性表LB 中而不存在于线性表 LA 中的数据元素插入到线性表 LA 中去。,上述问题可演绎为:,14,1从线性表LB中依次察看每个数据元素;,2依值在线性表LA中进行查访;,3若不存在,则插入之。,GetElem(LB, I, &e),LocateElem(LA, e, equal( ),ListInsert(LA, n+1, e),操作步骤:,15,GetElem(Lb, i, e); / 取Lb中第i个数据元素赋给eif (!LocateElem(La, e, equal( ) ) ListInsert(La, +La_len, e);/ La中不存在和 e

9、相同的数据元素,则插入之,void union(List ,for (i = 1; i = Lb_len; i+) , / union,在上述例子中,没有涉及到线性表的存储方式,只是伪代码,不是可以真正执行的程序。 通过伪代码,可以确立程序的基本思路,确定算法 算法形成后,从伪代码出发,结合存储结构,就可以编写出可以执行的程序,17,巳知线性表LA和线性表LB中的数据元素按值非递减有序排列,现要求将LA和LB归并为一个新的线性表LC,且LC中的元素仍按值非递减有序排列。 解:设三个指针:i指向LA中的元素ai, j指向LB中的元素bj,k指向LC元素ck。LC中的元素ck=ai(ab)。 移动

10、相应指针,例 2-2,i,j,k,比较ai和bj ai=bj, ck=ai, k递增,i递增 否则ck=bj, k递增,j递增 重复1.3,i到表尾,或者j到表尾 将a表或b表剩余元素之一添加到c表中,19,void Mergelist(List La,List Lb,List / end while,20,while(i=La_len)/Lb为空的情况GetElem(La,i+,ai); ListInsert(Lc,+k,ai);while(j=Lb_len)/La为空的情况GetElem(Lb,j+,bj); ListInsert(Lc,+k,bi);/ end Mergelist,21,

11、2.2 线性表的顺序表示和实现,对C语言必要复习,22,一、 什么是指针? 指针如何引用?,1 什么是指针?,int i=3;,int *pointer; pointer=,3001,23,如:用指针pi,pj,pk来存放i,j,k的地址,指针: 是指针变量的值(是地址,如2000、2004、2008),指针变量: 用于存放指针(地址)的变量(pi,pj,pk)。,pipjpk,ijk,200020042008,24,定义方法: 数据类型 *指针变量名 例 int *p; 数据类型 *指针变量名=变量地址 例 int *p=,其中1) 数据类型:指针变量所指向目标单元的值的类型。 2) * :

12、指针变量的定义符 3) 变量名 :目标变量在内存中的位置(地址),25,2 与指针相关的运算符,(1)&:取地址运算符:作用:用于变量名之前,表示该变量的存储地址。,(2)*:指针运算符(间接访问) 作用:用于指针变量名之前,获取该指针所指单元的值。,例如,定义和语句如下:int i=10;int *p,*p1; p=,p表示指针部分,属于指针类型,其内容为变量i的存储地址;,*p表示指针所指的变量部分,属于int类型。,p1指向p所指的变量,问:该段程序运行后i的值是多少?,26,需要注意的是:“*”操作符在指针上的两种用途要区分开:定义或声明时,建立一个指针;执行时间接引用一个指针。,定义

13、指针,间接引用一个指针,例如:int x,*p; p=,27,3 重要概念: 指针变量也有各种类型,但指针变量的值只能是整型值(地址)。,point= (),不允许直接对指针变量赋常量值。,如: int *point;,point=1000; (),只能给指针变量一个具有地址属性的值。,如: int x,*point;,指针的自增运算:int i;int *p;i = 10;p = ,29,1 结构体类型的认识,二、 结构数据类型的C表示法,上海,578,87.6.13,女,赵六,060411136,山东,550,86.12.1,男,王五,060411135,浙江,562,86.13.25,女

14、,李四,060411102,江苏,584,87.4.19,男,张三,060411101,生 源,成 绩,出生日期,性 别,姓 名,学 号,30,这是一个二维表,但却无法用二维数组来描述它,原因是用来描述学生信息的五项数据类型各不相同。能否将一个学生的信息作为一个完整的类型存放呢?为了能方便地处理此类问题,在C语言中,规定了一种新的数据类型“结构体类型”,可有效地表示类型互异又逻辑相关的数据实体。,typedef struct student int num;char name20;char gender;int age;int score;char placeofbirth100; node,

15、*pointer;/结构体别名,定义形式为:typedef struct 结构体名结构体各成分定义;结构体别名;,结构体变量说明, 例1:student wang;/说明了一个结构体变量wang 例2:node wang;/同上 例3:student st48;/说明了一个数组变量st,含48个数组元素,每个元素是结构类型,33,指向结构类型的指针变量,可说明为:node *p, *q; /或用 student *p , *q; /或用 pointer p , q; /注:上面已经定义了node为用户自定义的student类型。当把一个结构体变量的起始地址赋值给一个指针变量时,该指针就指向这个

16、结构体变量,该指针为结构体类型指针。,34,简单例子typedef struct student int num ; char name20;float score; ;/结构体定义部分student wang, stud3;student *p,*q;/结构体说明部分,35,令p=则指针的指向关系如图所示:,36,三、介绍C的三个有用的库函数/算符 (在 中定义,vc6.0也有定义): sizeof(x)计算变量x的长度(字节数); malloc(m) 开辟m字节长度的地址空间,并返回这段空间的首地址; 例1:p = (int *) malloc(5*sizeof(int); 例2:T =

17、(ElemType *) malloc(3*sizeof(ElemType); free(p) 释放指针p所指变量的存储空间,即彻底删除一个变量。,分隔符,38,用一组地址连续的存储单元 依次存放线性表中的数据元素,顺序存储结构,39,元素地址计算方法: LOC(ai)=LOC(a1)+(i-1)*L LOC(ai+1)=LOC(ai)+L 其中:L: 一个元素占用的存储单元个数LOC(ai): 线性表第i个元素的地址 特点:实现逻辑上相邻物理地址相邻实现随机存取 实现:可用C语言的一维数组实现,顺序存储结构,初始元素 下标,初始元素 下标,40,设有一维数组,下标的范围是1到10,每个数组元

18、素用相邻的个字节存储。存储器按字节编址,设存储数组元素1的第一个字节的地址是,则4的第一个字节的地址是,113,因此:LOC( M4 ) = 98 + 5 (4-1) =113,解:已知地址计算通式为:,LOC(ai) = LOC(a1) + L *(i-1),41,顺序表的 C 语言描述,typedef struct SqList ; / 俗称 顺序表,#define LIST_INIT_SIZE 80 / 线性表存储空间的初始分配量 #define LISTINCREMENT 10 / 线性表存储空间的分配增量,ElemType *elem; / 存储空间基址,int length; /

19、当前长度,int listsize; / 当前分配的存储容量 / (以sizeof(ElemType)为单位),关于ElemType的说明:ElemType是自定义的数据类型,用typedef在类型定义部分给出定义,程序头部(预编译部分) #include #include #include #include #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0,一个C程序应该包含的部分,类型定义部分typedef int Status; typedef int ElemType;typedef struct Tripleint

20、 i,j;ElemType e; triple;后面还有函数定义部分、全局变量定义部分,最后是主函数定义,44,例 typedef struct ElemType int num;char name40;char author10;char publisher30;float price; ; ElemType libraryM;,数据元素不是简单类型时,可定义结构体数组,或动态申请和释放内存 ElemType *p= (ElemType *)malloc(M*sizeof(ElemType); free(p);,45,线性表的基本操作在顺序表中的实现,InitList(&L) / 结构初始化

21、,LocateElem(L, e, compare() / 查找,ListInsert(&L, i, e) / 插入元素,ListDelete(&L, i) / 删除元素,注意:C语言中的数组下标从“0”开始,因此,若L是 Sqlist类型的顺序表,则表中第i个元素是L.elemi-1。,46,顺序表的初始化,int InitList_Sq(SqList ,函数调用: main( ) sqlist L; InitList_Sq( L);,关于结构类型元素的调用 如前面的定义 typedef struct SqList ElemType *elem; int length; int listsi

22、ze; ; SqList L; L.length = 80 变量调用时用“.”调用数据域,变量指针时用“-”调用数据域,48,int LocateElem_Sq(SqList L, ElemType e)/ 在顺序表中查询第一个等于e的数据元素,/ 若存在,则返回它的位序,否则返回 ERROR / LocateElem_Sq,O( ListLength(L) ),算法的时间复杂度为:,i = 1; / i 的初值为第 1 元素的位序 p = L.elem; / p 的初值为第 1 元素的存储位置,while (i = L.length ,if (i L.length) return ERROR

23、; else return i;,49,3)插入 在线性表的第i个位置前插入一个元素,实现步骤: 将第n至第i 位的元素向后移动一个位置; 将要插入的元素写到第i个位置; 表长加1。 注意:事先应判断: 插入位置i 是否合法?表是否已满?,长度为n的线性表变为长度为n+1的线性表 (a1,a2,ai-1,ai,an),(a1,a2,ai-1,x,ai,an),50,Status ListInsert_Sq(SqList &L, int i, ElemType e) /在顺序表L的第 i 个元素之前插入新的元素e,1iL.length+1,if (L.length = L.listsize) /

24、 当前存储空间已满,增加分配 newbase = (ElemType *)realloc(L.elem, (L.listsize+LISTINCREMENT)*sizeof (ElemType);if (!newbase) printf(“分配空间失败”); return ERROR; L.elem = newbase; / 新基址L.listsize += LISTINCREMENT; / 增加存储容量 ,if (i L.length+1) printf(“插入位置错误“);return ERROR; ,51,for (j= L.length-1; j = i-1; j - -) L.ele

25、mj+1=L.elemj; / 插入位置及之后的元素后移 L.elemi-1 = e; / 插入e L.length+; / 表长增1 return OK; ,52,考虑移动元素的平均情况:,假设在第 i 个元素之前插入的概率为 , 则在长度为n 的线性表中插入一个元素所需移动元素次数的期望值为:,若假定在线性表中任何一个位置上进行插入的概率都是相等的,则移动元素的期望值为:,O(n),53,实现步骤: 将第i +1至第n 位的元素向前移动一个位置; 表长减1。 注意:事先需要判断,删除位置i 是否合法?,4)删除 删除线性表的第i个位置上的元素,使:长度为n的线性表变为长度为n-1的线性表。

26、(a1,a2,ai-1,ai,ai+1,an),(a1,a2,ai-1,ai+1,an),54,Status ListDelete_Sq(SqList &L, int i, ElemType &e) / ListDelete_Sq,for (j=i; j=L.Length-1;j+) L.elmej-1=L.elemj; / 被删除元素之后的元素前移 -L.length; / 表长减1 return 1;,算法时间复杂度为:,O( ListLength(L),e = L.elemi-1; / 被删除元素的值赋给 e,if (i L.length) printf(“删除位置错误“);return

27、 0; ,55,考虑移动元素的平均情况:,假设删除第 i 个元素的概率为 , 则在长度为n 的线性表中删除一个元素所需移动元素次数的期望值为:,若假定在线性表中任何一个位置上进行删除的概率都是相等的,则移动元素的期望值为:,O(n),56,小结,线性表顺序存储结构特点:逻辑关系上相邻的两个元素在物理存储位置上也相邻; 优点:可以随机存取表中任一元素O(1);存储空间使用紧凑 缺点:在插入,删除某一元素时,需要移动大量元素O(n);预先分配空间需按最大空间分配,利用不充分;表容量难以扩充 为克服这一缺点,我们引入另一种存储形式:,链式存储结构,分隔符,58,特点: (1)用一组任意的存储单元存储

28、线性表的数据元素; (2)利用指针实现了用不相邻的存储单元存放逻辑上相邻的元素; (3)每个数据元素ai,除存储本身信息外,还需存储其直接后继的信息。结点 数据域:元素本身信息 指针域:指示直接后继的存储位置,线性表的链式存储结构,59,data,next,结点(*p),定义:结点中只含一个指针域的链表,也叫单链表。Typedef struct LNodeElemType data;struct LNode *next; Lnode *LinkList; 若声明 LNode *p; 则:p=malloc(sizeof(LNode);表示生成一个新结点。free(p);表示系统回收p结点。,单链

29、表,60,一个线性表的逻辑结构为:(ZHAO,QIAN,SUN,LI,ZHOU,WU,ZHENG,WANG),其存储结构用单链表表示如下,请问其头指针的值是多少?,例:,答:头指针是指向链表中第一个结点的指针,因此关键是要寻找第一个结点的地址。,31,头指针的值是31,61,上例链表的逻辑结构示意图有以下两种形式:,区别: 无头结点 有头结点,62,讨论1. 在链表中设置头结点有什么好处?,讨论2. 如何表示空表?,头结点即在链表的首元结点之前附设的一个结点,该结点的数据域中不存储线性表的数据元素,其作用是为了对链表进行操作时,可以对空表、非空表的情况以及对首元结点进行统一处理,编程更方便。,

30、无头结点时,当头指针的值为空时表示空表; 有头结点时,当头结点的指针域为空时表示空表。,63,头结点:在单链表第一个结点前附设的一个结点。 头结点指针域为空表示线性表为空。,64,单链表操作的实现,GetElem(L, i, e) / 取第i个数据元素,ListInsert(&L, i, e) / 插入数据元素,ListDelete(&L, i, e) / 删除数据元素,ClearList(&L) / 重置线性表为空表,CreateList(&L, n)/ 生成含 n 个数据元素的链表,65,(1)在链表的头部插入结点建立单链表链表与顺序表不同,它是一种动态管理的存储结构,链表中的每个结点占用

31、的存储空间不是预先分配,而是运行时系统根据需求而生成的,因此建立单链表从空表开始,每读入一个数据元素则申请一个结点,然后插在链表的头部。,建立单链表,66,例如:逆位序输入 n 个数据元素的值,建立带头结点的单链表。,操作步骤:,一、建立一个“空表”;,二、输入数据元素an,建立结点并插入;,三、输入数据元素an-1,建立结点并插入;,an,an,an-1,四、依次类推,直至输入a1为止。,67,int CreateList_L(LinkList &L, int n) / 逆序输入 n 个数据元素,建立带头结点的单链表 / CreateList_L,算法的时间复杂度为:,O(Listlengt

32、h(L),L = (LinkList) malloc (sizeof (LNode); L-next = NULL; / 先建立一个带头结点的单链表,for (i = 1; idata); / 输入元素值p-next = L-next; L-next = p; / 插入 return OK;,68,在单链表的尾部插入结点建立单链表,以上算法读入的数据元素的顺序与生成的链表中元素的顺序是相反的,若希望次序一致,则用尾插入的方法。因为每次是将新结点插入到链表的尾部,所以需加入一个尾指针 r 用来始终指向链表中的尾结点,以便能够将新结点插入到链表的尾部,如下图所示。,69,在单链表的尾部插入结点建立

33、单链表,操作步骤:,一、建立一个“空表”,尾指针指向头结点,二、输入数据元素a1,建立结点,插入在尾指针的后面,尾指针指向a1结点;,三、输入数据元素a2,建立结点,插入在尾指针的后面,尾指针指向a2结点;,四、依次类推,直至输入an为止。,void CreateList_L(LinkList /for 依次插入n个数据元素 /CreateList_L,71,查找操作 (1) 按序号查找 Get_Linklist(L,i) 算法思路:从链表的第一个元素结点起,判断当前结点是否是第i个,若是,则返回该结点的指针,否则继续后一个,表结束为止。没有第个结点时返回空。,72,int GetElem_L

34、(LinkList L,int i,ElemType ,73,(2) 按值查找即定位 Locate_LinkList(L,e)算法思路:从链表的第一个元素结点起,判断当前结点其值是否等于e,若是,返回该结点的指针,否则继续后一个,表结束为止。找不到时返回空。 算法如下: LNode * LocateElem_L(LinkList L,ElemType e)p=L-next; /初始化p指向第一个结点while(p ,74,插入 (1)后插结点: 设p指向单链表中某结点,s指向待插入的值为x的新结点,将*s插入到*p的后面。 操作如下: s-next=p-next; p-next=s;,p,s,

35、思考:Step1和2能互换么?,75,int ListInsert_L(LinkList L, int i, ElemType e) / L 为带头结点的单链表的头指针,本算法/ 在链表中第i个结点之前插入新的元素 e / LinstInsert_L,p = L; j = 0; while (p / i 大于表长或者小于1,s = (LinkList) malloc ( sizeof (LNode); / 生成新结点 s-data = e; s-next = p-next; p-next = s; / 插入 return OK;,算法的时间复杂度为:,O(ListLength(L),76,(2

36、)前插结点: 设指向链表中某结点,指向待插入的值为x的新结点,将*s插入到*p的前面。 q=L; while (q-next!=p)q=q-next; s-next=q-next; q-next=s;,s,p,q,先找到p的前驱结点再做插入,77,删除 (P30算法2.9)设q指向单链表中某结点,删除*q。首先要找到*q的前驱结点*p,然后完成指针的操作即可。 操作由下列语句实现: p-next=q-next; free(q);,free(q)语句不能少,否则丢内存,78,Status ListDelete_L(LinkList L, int i, ElemType &e) / 删除以 L 为

37、头指针(带头结点)的单链表中第 i 个结点 / ListDelete_L,算法的时间复杂度为:,O(ListLength(L),p = L; j = 0; while (p-next / 删除位置不合理,q = p-next; p-next = q-next; / 删除并释放结点 e = q-data; free(q); return OK;,79,void ClearList( / ClearList,算法时间复杂度:,O(ListLength(L),ClearList(此程序教材没有,但是比较有用),80,链表插入删除实例:两个链表的归并(P31算法2.12),算法要求: 已知:线性表 A

38、、B,分别由单链表 La , Lb 存储,其中数据元素按值非递减有序排列, 要求:将 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 ),81,算法设计:,算法主要包括搜索、比较、插入三个操作: 搜索:需要设立三个指针来指向La 、Lb和Lc链表; 比较:比较La和Lb指向结点数据的大小; 插入:将La和Lb中数据较小的结点插入新链表Lc 。,请注意链表的特点,仅改变指针便可实现数据的

39、移动,即“数据不动,指针动”,82,算法实现:,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=La-next; pb=Lb-next; Lc=pc=La; /初始化,?是条件运算符,为真则取第1项,Lc用的是La的头指针,只有Lb头指针空闲。,83,思考:,1、不用Lc,直接把La表插到Lb表中;或者把Lb

40、表插到La表中,该如何编程?,2、要求归并后的表中不能有重复的数据元素,该如何编程?,静态链表,对于那些没有指针类型的程序设计语言,可以用静态链表如右图1所示插入SHI,删除ZNENG后如右图2,(注意备用链),85,内容回顾,线性表的逻辑结构顺序表的表示和基本操作的实现单链表的表示和基本操作的实现,分隔符,87,循环链表 双向链表 链表应用,循环链表双向链表应用:一元多项式的表示及相加,88,将表中最后一个结点的指针域指向头结点(P-next=H;),这种形成环路的链表称为循环链表。,循环链表,空表条件:H-next=L,P,89,循环链表是表中最后一个结点的指针指向头结点,链表构成环状;

41、从表中任一结点出发均可找到表中其他结点,提高查找效率;,循环链表特点,90,循环链表的操作,操作与单链表基本一致, 循环终止条件不同: 单链表: p-next=NULL 循环链表:p-next=H,91,循环链表中设立尾指针,找到第一个结点(rear-next-next)和最后一个结点 (rear)均需要常数时间,利用尾指针可以快速的合并两个表,93,linklist connect(linklist ,例、在单循环链表上实现将两个线性表(a1,a2,a3,an)和(b1,b2,b3,bn)链接成一个线性表的运算。,94,单链表只能查找结点的直接后继,不能查找直接前驱。要实现查找直接前驱,可以

42、给结点增加一个域,令其指向其直接前驱,这种有两个指针的链表称为双向链表。其特点是可以双向查找表中结点。,双向链表,typedef struct DuLNoeElemtype data;struct DuLNode *prior;struct DuLNode *next; DulNode,*DuLinklist;,类型定义,双向链表示例,97,双向循环链表的对称性,d-next-prior=d-prior-next=d;,很明显,在双向链表中不仅能直接找到结点的前驱, 也能直接找到结点的后继。,98,双向链表的操作特点:,“查询” 和单链表相同。,“插入” 和“删除”时需要同时修改两个方向上的指

43、针。,99,双向链表中结点的插入: 设p指向双向链表中某结点,x指向待插入的值为x的新结点,将*x插入到*p的前面。 操作如下: x-prior=p-prior; p-prior-next=x; x-next=p; p-prior=x;,Status ListInsert_DuL(DuLinkList L,int i,ElemType e) if(!p=GetElemP(L,i) ; / 在L中确定第i个元素的位置指针p return ERROR;/ p=NULL,即第i个元素不存在if(!s=(DuLinkList)malloc(sizeof(DuLNode)return OVERFLOW;

44、s-data=e;s-prior=p-prior; p-prior-next=s;s-next=p; p-prior=s;return OK;,101,双向链表中结点的删除: 设p指向双向链表中某结点,删除*p。 操作如下: p-prior-next=p-next; p-next-prior=p-prior; free(p);,Status ListDelete_DuL(DuLinkList L,int i,ElemType *e) if(!p=GetElemP_DuL(L,i) /* 在L中确定第i个元素的位置指针p */ return ERROR; /* p=NULL,即第i个元素不存在

45、*/e=p-data;p-prior-next=p-next;p-next-prior=p-prior;free(p);return OK;,103,链表的运算效率分析,1. 查找 因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为 O(n)。,2. 插入和删除 因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为 O(1)。,但是,如果要在单链表中进行前插或删除操作,由于要从头查找前驱结点,所耗时间复杂度为 O(n)。,空间效率分析,链表中每个结点都要增加一个指针空间,相当于总共增加了n 个整型变量,空间复杂度为 O(n)。,104,例:已知单链表L,写一算法,删

46、除其重复结点,即实现如下图2.23的操作。算法思路: 用指针p指向第一个数据结点,从它的后继结点开始到表的结束,找与其值相同的结点并删除之;p指向下一个;依此类推,p指向最后结点时算法结束。,105,void pur_LinkList(LinkList H) LNode *p,*q,*r;p=H-next; /*p指向第一个结点*/if(p=NULL) return;while (p-next) q=p; while (q-next) if (q-next-data=p-data) r=q-next; q-next=r-next; free(r); else q=q-next; p=p-nex

47、t; 该算法的时间性能为O(n2)。,算法,106,例:线性表 ( a1,a2,.,am) 改变成 (b1,b2,bn) 。,解题分析: 因为对链表来说,“插入”和“删除”仅需修改指针即可完成,并且由于前 m 个元素之间和后 n 个元素之间的链接关系分别都不需要改变,则算法的实际操作为: (1) 从链表中删除(a1,a2,.,am); (2) 将(b1,b2,bn)链接到头结点之后; (3) 将(a1,a2,.,am)链接到bn 之后。,107,void exchange_L( SLink / 将 a1 结点链接到 bn 结点之后 / if(p) / if(m) / exchange_L,108,一元多项式的表示及相加,109,在计算机中,可以用一个线性表来表示:P = (p0, p1, ,pn),一元多项式,但是对于形如S(x) = 1 + 3x10000 2x20000 的多项式,上述表示方法是否合适?,110,一般情况下的一元稀疏多项式可写成Pn(x) = p1xe1 + p2xe2 + + pmxem 其中:pi 是指数为ei 的项的非零系数,0 e1 e2 em = n,可以下列线性表表示: (p1, e1), (p2, e2), , (pm,em) ),

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报