1、第四章 线性表、堆栈和队列,第四章 线性表、堆栈和队列4.1 线性表的定义和基本操作4.2 线性表的存储结构4.3 堆栈和队列,4.1 线性表的定义和基本操作 4.1.1 线性表的定义 例1 英文字母表 ( A,B,C,Z )整数序列 ( 1,78,9,12,10)例2 某班学生健康情况登记表。学号 姓名 性别 年龄 健康情况01 张军 男 18 一般 02 陈红 女 17 良好03 陈军 男 19 神经衰弱 ,线性表定义:一个线性表是由零个或多个具有相同类型的结点组成的有序集合。用(a0,a1,an-1)来表示一个线性表。当n0时,a0称为表的始结点,an-1称为表的终结点,当n=0时,线性
2、表中有零个结点,称为空表。线性表的逻辑结构:线性结构,线性表的操作 (1)随机存取:存取下标为k的结点。 (2)插入:在下标为k的结点前(或后)插入一个新结点 (3)删除:删除下标为k的结点。 (4)查找:寻觅具有特定域值的结点。 (5)归并、分拆、复制、计数、排序。,第四章 线性表、堆栈和队列 4.2 线性表的存储结构4.2.1 顺序存储结构 4.2.2 链接存储结构单链表4.2.3 循环链表4.2.4 双向循环链表,4.2 线性表的存储结构,4.2 线性表的存储结构 4.2.1 顺序存储结构顺序存储:用一组连续的存储空间依次存储线性表的元素。实现顺序存储的最有效方法是使用一维数组。例如 :
3、线性表(a0,a1 , , an-1)。可以使用一个数组an来存放此线性表。,包含4个结点的线性表A4在内存中的表示,其中每个结点占2个连续的字节,第一个结点A0的首地址为302,例 :线性表(a0,a1 , , an-1)。可以使用一个数组an来存放此线性表。,Loc(ak)= Loc (a0) + k*c, 特点:其逻辑顺序与物理顺序相同。顺序存储,随机存取,顺序存储的线性表的基本运算1、插入例 在顺序表(12,13,21,24,28,30,42,77) 中,插入元素 25。问题:此时,线性表的逻辑结构发生什么变化?位置关系发生变化长度增1,在下标为k的结点后插入一个新结点 /ADL描述算
4、法 Insert(A,n,k,x)Insert1k是否合法IF (kn) THEN PRINT(“overflow”)ELSE ( FOR i= n TO k+1 STEP -1 DOAi+1 Ai.Ak+1 x.n n+1.). ,时间复杂性分析: 插入操作的基本运算是: 元素移动 Dn中有多少种可能的输入呢?n+1种(n+1个位置可以发生插入) 设每种输入发生的频率相等:1/(n+1) 则期望复杂性为: E(n)=(n+(n-1) + +1+0) /(n+1) =n/2,2、删除例 在顺序表(12,13,21,24,28,30,42,77) 中,删除元素 24。,问题:此时,线性表的逻辑结
5、构发生什么变化?位置关系发生变化长度减1,删除下标为k的结点 /ADL描述算法 Delete(A,n,k)Delete1检查k是否合法IF (kn)THEN PRINT(“error”)ELSE ( FOR i=k+1 TO n DOAi-1 Ai.n n-1.) ,时间复杂性分析:删除操作的基本运算是: 元素移动Dn中有多少种可能的输入呢?n种(n个位置可以发生删除)设每种输入发生的频率相等:1/n则期望复杂性为:E(n)=(n-1) /n +1/ n +0/ n =(n-1)/2, 结论:线性表的顺序存储结构优点:空间利用率高,简单、易于实现,可以随机访问表中任一元素,存取速度快。缺点:插
6、入和删除结点,要调整一批节点的地址。问题:由于线性表中元素的数目可以改变,因此定义数组时要做如何的考虑呢?定义足够大的数组。,4.2.2 链接存储结构单链表1、单链表的定义2、单链表的基本操作3、单链表结点类(Node)4、单链表的构造5、链表类(LinkedList),4.2.2 链接存储结构单链表1、单链表的定义 链式存储:用一组任意存储单元存储线性表的数据元素。,例 将线性表(a3,a4,a5 ),以链表的形式存 储在内存中。,002,500, 单链表的结点结构: 单链表的定义:每个结点只含有一个链接域的 链表叫单链表。,单链表的存储映像, 特点:逻辑顺序与物理顺序可以相同也可 以不同。
7、,链表有头节点、尾节点、头指针(head)。判断表尾的条件:p next = = NULL空表的条件:head = = NULL,4.2.2 链接存储结构单链表1、单链表的定义2、单链表的基本操作3、单链表结点类(Node)4、单链表的构造5、链表类(LinkedList),在链表中,删除一个结点或插入一个结点,只需改变一个或两个相关结点的指针,不对其它结点产生影响。, 插入:, 删除:,4.2.2 链接存储结构单链表1、单链表的定义2、单链表的基本操作3、单链表结点类(Node)4、单链表的构造5、链表类(LinkedList),(1) Node类声明:template class Node
8、 private:Node * next;public:T data;,/ 构造函数 Node ( const T,(2) Node类的实现 构造函数template Node : Node(const T& item ,Node * ptrnext):data(item),next(ptrnext) , 返回当前结点的 next 域的值template Node * Node : nextNode (void) const return next;, 在当前结点之后插入结点 p /ADL描述算法InsertAfter(this ,p) IA1 将当前结点的next域值赋给P的next域nex
9、t(p) next(this).IA2 将P赋给当前结点的next域next(this) p . ,在当前结点之后插入结点 p /C+描述 template void Node : InsertAfter(Node *p) p next = next ;next = p ; , 删除当前结点的后继结点 /ADL描述 算法 DeleteAfter(this . tempptr)DA1 this的next域值 = NULL?IF next(this)= NULL THEN tempptr NULL .ELSE(tempptr next(this).next(this) next(tempptr)
10、. ) ,删除当前结点的后继结点/C+描述template Node * Node :DeleteAfter(void) if( next= = NULL)return NULL;Node * tempptr = next;next = tempptr next; return tempptr;,4.2.2 链接存储结构单链表1、单链表的定义2、单链表的基本操作3、单链表结点类(Node)4、单链表的构造5、链表类(LinkedList),在 Node类基础上, 构造单链表1)创建新结点2)表头插入结点3)遍历链表4)表尾插入结点,1)创建一个data为 item, next为nextptr的
11、结点算法GetNode(item ,nextptr . NewNode)GN1 为新结点申请空间NewNode AVAIL . GN2 为结点诸域赋值data(NewNode) item. next(NewNode) nextptr. ,item,nextptr,# include # include “stdlib.h”template Node * GetNode(const T ,2)表头插入:在头指针为 head 的链表中,插入data 域为 item 的新结点,并将其作为头结点。算法InsertFront(head,item)IF1 调用函数GetNodeGetNode(item,h
12、ead. newNode). IF2 head指向新结点head newNode. ,template void InsertFront (Node * ,例4.1 构造一个有n个结点的单链表,第i个结点的数据为给定数组an中ai-1的值。,代码段一:Node *head=NULL;for( i=n-1;i=0;i-)head=GetNode(ai,head); 代码段二:Node *head=NULL;for( i=n-1;i=0;i-)InsertFront(head,ai);,3) 遍历链表 遍历:按一定次序访问所有节点,且每个节点恰被访问一次。 算法PrintList(head)/输出
13、头指针为head的链表,每输出5个元素换行 PL 1 取表头,计数器初始化currptr head . count 0 .,currptr,PL 2 遍历并输出链表结点的data值WHILE(currptr NULL) DO ( PRINT( data(currptr). count count + 1.IF(MOD(count,5)= 0 ) THEN PRINT .currptr next(currptr).),currptr,currptr, 遍历链表/C+template void printList(Node * head) Node * currptr = head;count =
14、 0;while ( currptr != NULL ) cout ( currptr data) “ ” ;if( (count + ) % 5 = = 0 ) cout endl;currptr = currptr nextNode( ); ,4) 表尾插入结点在头指针为head的链表尾部插入data域值为item的结点; 算法InsertRear(head ,item) IR1 取头指针 currptr head .,currptr,IR2 currptr = NULL?IF currptr = NULL THEN InsertFront(head ,item),head=NULL cu
15、rrptr=NULL,item,head,NULL,IR2 currptr = NULL?IF currptr = NULL THEN InsertFront(head ,item) .ELSE( WHILE(next(currptr) NULL) DO currptr next(currptr).GetNote(item,NULL . newNode).InsertAfter(currptr ,newNode). ),currptr,表尾插入结点/C+template void InsertRear(Node * ,例4.1 构造一个有n个结点的单链表,第i个结点的数据为给定数组an中ai-
16、1的值。,代码段三:Node *head=NULL;for( i=0;in;i+)InsertRear(head,ai);,4.2.2 链接存储结构单链表1、单链表的定义2、单链表的基本操作3、单链表结点类(Node)4、单链表的构造5、链表类(LinkedList),定义一种链表类LinkedList ,将链表的基本操作视为链表类的成员函数,并将其封装在类中。,类LinkedList所涉及到的数据成员:头指针:front尾指针:rear当前指针:currptr当前结点的前驱结点指针:prevptr链表中结点个数:size当前结点在链表中的位置:position,/ 令当前指针指向表头 voi
17、d Reset(int pos=0); / 令当前指针指向原当前结点的后继结点 void Next(void); / 判断当前指针是否指向表尾结点 int EndOfList(void)const ; / 插入一个data域值为item的结点 void InsertFront(const T,例4.2 按表L1在前,表L2在后的次序,实现两者的连接。 template void JointLists(LinkedList ,while( ! L2.EndOfList( ) ) L1.InsertRear(L2.Data( );L2.Next( ) ; ,单链表总结优点:插入、删除操作方便;共享
18、空间好。 缺点:随机访问困难; (从任一结点出发,只能访问其后的结点; 只能从头结点开始,才能扫描表中全部结点),4.2.3 循环链表1 循环链表的定义和结构 定义:在单链表中,使其最后一个结点的指针又指回到第一个结点,则这样的链表叫循环链表。单链表表头结点:第一个结点循环链表表头结点:哨位结点(header),header,判断表尾的条件:单链表:p next = = NULL 循环链表:p next = = Header,Header,判断空表的的条件:单链表:Head=NULL循环链表:Header next Header,header,2. 循环链表结点类 CNode的定义 声明tem
19、plate class CNode, private:CNode * next;public:T data;CNode (void); / 生成哨位结点CNode (const T,3、实现/ 删除当前结点的后继结点 ADL描述 算法DeleteAfter(this. tempptr) DA1 链表为空?IF next(this)= this THENtempptr NULL .,ELSE ( IF next(this)header THEN (tempptr next(header). next(header) next(tempptr).)ELSE(tempptr next(this).
20、next(this) next(tempptr). ) ) ,4.2.4 双向循环链表1 问题的提出在循环链表中访问结点p的前趋结点tempptr next(p).WHILE next(tempptr)p DOtempptr next(tempptr).,2 双向循环链表的结构结点结构:链表的结构,Header left = = Header right = = Headerp right left = p left right = p,Header,3 循环双链表结点类DNode定义 声明template class DNode private:DNode * left; DNode * r
21、ight;,public:T data;DNode (void);/生成哨位节点DNode (const T,实现 构造函数template Node : DNode(const T& item):data(item),left(this),right(this) ,item,在当前结点(this)之后插入结点 Pleft(right(this) P .right(P) right(this).left(P) this. right(this) P .,在当前结点(this)之后插入结点 P 算法InsertRight(this ,P) IR1 令当前结点的右结点的左指针指向Node(P)le
22、ft(right(this) P . IR2 令Node(P)的右指针指向当前结点的右结点right(P) right(this). IR3 令Node(P)的左指针指向当前结点left(P) this. IR4 令当前结点的右指针指向Node(P)right(this) P ,在当前结点(this)之前插入结点P left(P) left(this) right(left(this) P. right(P) this . left(this)P .,在当前结点(this)之前插入结点 P 算法InsertLeft(this ,P) / InsertLeft用IL简记 IL1 令Node(P)
23、的左指针指向当前结点的左结点left(P) left(this). IL2 令当前结点之左结点的右指针指向Node(P)right(left(this) P. IL3 令Node(P)的右指针指向当前结点right(P) this . IL4 令当前结点的左指针指向Node(P)left(this)P , 删除当前结点(this) 算法DeleteNode(this ) DN1 令当前结点的左结点的右指针指向当前结点的右结点right(left(this) right(this). DN2 令当前结点的右结点的左指针指向当前结点的左结点left(right(this) left(this),练
24、习题: 1.对于线性表的两种存储结构,若线性表的总数基本稳定,且很少进行插入和删除操作,但要求以最快的速度存取线性表中的元素,那么应选用何种存储结构?试说明理由。若线性表的总数不稳定,且经常进行插入和删除操作,那么应选用何种存储结构?试说明理由。,2. 在链表中查找data域值为x的结点算法Find(head,x.currptr) F1取头指针currptr head. F2查找WHILE (currptrNULL AND data(currptr) x) DOcurrptr next(currptr). ,3.单链表应用:一元多项式及其相加在多项式的链表表示中每个结点增加了一个数据成员link,作为链接指针。优点:多项式的项数可以动态地增长,不 存在存储溢出问题。插入、删除方便,不移动元素。,多项式链表的相加,AH = 1 - 10x6 + 2x8 +7x14 BH = - x4 + 10x6 - 3x10 + 8x14 +4x18,作 业,1、已知长度为n的线性表A(a1,a2,an)采用顺序存储结构,请写一算法,将线性表转换为A(an, a2,a1),要求转换过程中用尽可能少的存储空间。 2、编写算法,在头指针为head的单链表中,删除域值为x的结点。 3、68页4-1,