收藏 分享(赏)

第2章 线性表new.ppt

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

1、07:39,翟音,数据结构,07:39,近4周 上课 内容,第2章 线性表 第3章 栈和队列 第4章 字符串,线性结构,若结构是非空有限集,则有且仅有一个开始结点和一个终端结点,并且所有结点都最多只有一个直接前趋和一个直接后继。,可表示为:(a1 , a2 , , an),线性结构的定义:,(逻辑、存储和运算),07:39,线性结构的特点:, 只有一个首结点和尾结点; 除首尾结点外,其他结点只有一个直接前驱和一个直接后继。,线性结构表达式:(a1 , a2 , , an),线性结构包括线性表、堆栈、队列、字符串、数组、广义表等等,其中,最典型、最常用的是,线性表,简言之,线性结构反映结点间的逻

2、辑关系是 一对一 的,07:39,第2章 线性表,1. 了解线性结构的特点 2.掌握顺序表的定义、查找、插入和删除 3.掌握链表的定义、查找、插入和删除 4.能够从时间和空间复杂度的角度比较两种存储结构的不同特点及其适用场合,教学目标,07:39,2.1 线性表的概念(ADT) 2.2 线性表的顺序表示和实现(顺序表) 2.3 线性表的链式表示和实现(链表) 2.4 线性表实现方法的比较,教学内容,07:39,(a0, a1, ai-1,ai, ai1 ,, an-1),线性表的定义:用数据元素的有限序列表示,n=0时称为,数据元素,线性起点,ai的直接前趋,ai的直接后继,下标,是元素的序号

3、,表示元素在表中的位置,n为元素总个数,即表长,空表,线性终点,2.1 线性表的概念 2.1.1 线性表的抽象数据类型,07:39,例1 分析26 个英文字母组成的英文表,( A, B, C, D, , Z),例2 分析学生情况登记表,数据元素都是记录; 元素间关系是线性,数据元素都是字母; 元素间关系是线性,同一线性表中的元素必定具有相同特性,07:39,template class list / 线性表类模板list,模板参数Tvoid Clear( ); / 置空线性表bool isEmpty( ); /线性表为空时,返回truebool append(const T value) ;

4、/ 尾附函数,在表尾加新元素value,表长加1bool insert(const int p, const T value) ; / 插入函数,在位置p上插入元素value,表长加1bool delete(const int p);/ 删除函数,删去位置p上的元素,表长减1bool getValue(const int p,T /读取,返回位置p的元素值到变量value中bool setValue( ) /用value修改位置p的元素值bool getPos( ) /把值为value的元素位置返回到变量p中;,线性表的抽象数据类型的定义,模板是C+的软件复用的功能之一,此处定义的是模板类,T

5、通常是代表要处理的数据元素的类型,2.1.2 线性表的存储结构,07:39,线性表的存储结构主要有两类:(1)顺序表定长存储结构(数组)(2)链表变长存储结构(指针),07:39,2.1.3 线性表运算分类,(1)创建线性表的一个实例。(2)线性表的析构函数list( ),消除线性表实例并释放所占空间。(3)获取有关当前线性表的信息,包括由内容寻找位置、由位置读取元素内容等,不改变线性表的内容。(4)访问线性表并改变线性表的内容或结构;例如更新制定元素内容、添加元素、删除元素、清空线性表等。(5)辅助管理操作,例如求表的当前长度等。,07:39,线性表的顺序表示又称为顺序存储结构或顺序映像。,

6、简言之,逻辑上相邻,物理上也相邻,顺序存储方法:用一组地址连续的存储单元依次存储线性表的元素,可通过数组an来实现。,顺序存储定义:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。,2.2 线性表的顺序表示和实现(顺序表) 2.2.1 顺序表的类定义,07:39,07:39,Tempate Class arrList:public Listprivate:T *aList; /T类型的指针,存储线性表实例int maxSize; /实例的最大长度int curLen; /实例的当前长度int position; / 当前处理位置public:arrList (const int

7、size) maxSize = size;aList = new TmaxSize;curLen = position=0; arrList ( ) delete aList; bool Append (const T value) aListposition=value;position+;curLen+; ;,顺序表的类定义,07:39,2.2.2 顺序表的运算实现,查找-又称为检索 (根据指定数据查找,返回数据所在的位置)按值查找和按位置查找,插入(插在第 i 个结点之前),删除(删除第 i 个结点),07:39,查找(根据指定数据查找,返回数据所在的位置),顺序查找图示,25 34 5

8、7 16 48 09,0 1 2 3 4 5,data,查找 16,i,25 34 57 16 48 09,i,25 34 57 16 48 09,i,25 34 57 16 48 09,i,查找成功,07:39,25 34 57 16 48,0 1 2 3 4,data,查找 50,i,25 34 57 16 48,i,25 34 57 16 48,i,25 34 57 16 48,i,25 34 57 16 48,i,查找失败,在顺序表中查找值为value的元素的位置 /在表中查找值为value的元素,成功则返回true, /并将该元素所在的下标记录在参数p中, /失败则返回false,t

9、emplate bool arrList : getPos ( int ,查找算法时间效率分析 ?,引用参数,07:39,查找成功的平均比较次数(pi为各项的查找概率) 若查找概率相等,则 查找不成功 数据比较 n 次,查找算法时间效率分析,07:39,25 12 47 89 36 14,0 1 2 3 4 5 6 7 8,25 12 47 99 89 36 14,99插入,25 12 47 89 3614,25 12 47 8936 14,25 12 4789 36 14,插第 3 个结点之前,移动 63 次 插在第 i 个结点之前,移动 n-i 次,插入(插在第 i 个结点之前),07:3

10、9,(1)判断插入位置i 是否合法。 (2)判断顺序表的存储空间是否已满。 (3)将第n-1至第i 位的元素依次向后移动一个位置,空出第i个位置。 (4)将要插入的新元素e放入第i个位置。 (5)表长加1,插入成功返回TRUE。,【插入算法思想】,07:39,若插入在尾结点之后,则根本无需移动(特别快); 若插入在首结点之前,则表中元素全部后移(特别慢); 若要考虑在各种位置插入(共n+1种可能)的平均移动次数,该如何计算?,算法时间主要耗费在移动元素的操作上,【插入算法分析】,【插入算法描述】算法2.4 书P32,07:39,25 12 47 89 36 14,0 1 2 3 4 5 6 7

11、 8,25 12 47 3614,25 12 47 36 14,25 12 47 36 14,删除,删除(删除第 i 个结点),删除第 3 个结点,移动 63+1 次 删除第 i 个结点,移动 n-i +1次,07:39,(1)判断删除位置i 是否合法(合法值为0in-1) (2)将欲删除的元素保留在e中。 (3)将第i+1至第n-1 位的元素依次向前移动一个位置 (4)表长减1,删除成功返回OK。,【删除算法思想】,07:39,若删除尾结点,则根本无需移动(特别快); 若删除首结点,则表中n-1个元素全部前移(特别慢); 若要考虑在各种位置删除(共n种可能)的平均移动次数,该如何计算?,算法

12、时间主要耗费在移动元素的操作上,【删除算法分析】,【删除算法描述】算法2.5 书P33,07:39,显然,顺序表的空间复杂度S(n)=O(1) (没有占用辅助空间),查找、插入、删除算法的平均时间复杂度为O(n),07:39,(1)利用数据元素的存储位置表示线性表中相邻数据元素之间的前后关系,即线性表的逻辑结构与存储结构一致,顺序表(顺序存储结构)的特点,这种存取元素的方法被称为随机存取法,(2)在访问线性表时,可以快速地计算出任何一个数据元素的存储地址。因此可以粗略地认为,访问每个元素所花时间相等,顺序表的优缺点,缺点: 在插入、删除某一元素时,需要移动大量元素 浪费存储空间 属于静态存储形

13、式,数据元素的个数不能自由扩充,优点: 存储密度大(结点本身所占存储量/结点结构所占存储量) 可以随机存取表中任一元素,07:39,2.3 链表,链式存储结构 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻,线性表的链式表示又称为非顺序映像或链式映像。,07:39,如何实现?,通过指针来实现,单链表的存储映像,free,(a) 可利用存储空间,a0,a2,a1,a3,free,head,(b) 经过一段运行后的单链表结构,07:39,例 画出26 个英文字母表的链式存储结构,链式存储结构:,逻辑结构:( a, b, ,y, z),链式存储结构,特点每个元素(表项)由结点

14、(Node)构成。线性结构 结点之间可以连续,可以不连续存储 结点的逻辑顺序与物理顺序可以不一致 表可扩充,单链表 (Singly Linked List),32,单链表的结构定义,在C中定义单链表的结构十分简单:typedef int T; /结点数据的类型typedef struct node /结点结构定义T data; /结点数据域struct node *link; /结点链接指针域 LinkNode; /结点命名这是一个递归的定义。 在结构定义时不考虑操作,以后在定义和实现链表操作时直接使用结构的成分。,33,单链表的类定义,使用面向对象方法,要把数据与操作一起定义和封装,用多个类

15、表达一个单链表。链表结点(Link)类链表(InkList)类 定义方式复合方式嵌套方式继承方式结构方式, 链表结点的类定义,template class Link public:T data; /结点的数据域Link * next; /结点的指针域Link( const T info, const Link * nextValue= NULL)/具有两个参数的Link构造函数 data =info;next =nextValue;Link( const Link * nextValue)/具有一个参数的Link构造函数 next =nextValue; ;,07:39,头指针head和尾指针

16、tail,tail,指针head指向单链表开始结点的指针。,指针tail指向单链表尾结点的指针。加速对链表尾端的访问,append( )可在常数时间内完成。,template class InkList : public Link /继承结点类 private:Link * head,*tail; /定义头、尾指针Link * setPos(const int p); /返回单链表InkList中指向第p个元素的指针值 public:InkList( ) /用class Link的构造函数来初始化head和tailhead=tail= new Link ;linkList( ) /析构函数,释

17、放链表Link *tmp;while(head!=NULL) /从头结点开始,释放结点tmp=head;head=head-next;delete tmp; ;, 链表的类定义,添加头结点的作用?,07:39,讨论1. 如何表示空表?,有头结点时,当头结点的指针域为空时表示空表,带头结点:head-next=Null 不带头结点:head=Null,07:39,讨论2. 在链表中设置头结点有什么好处?,便于首元结点的处理 首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其它位置一致,无须进行特殊处理;,便于空表和非空表的统一处理 无论链表是否为空,头指针都是指向头结点的非

18、空指针,因此空表和非空表的处理也就统一了。 例如在第一个结点前插入结点的操作,07:39,讨论3. 头结点的数据域内装的是什么?,头结点的数据域可以为空,也可存放线性表长度等附加信息,但此结点不能计入链表长度值。,头结点的数据域,07:39,思考:顺序表里如何找到第i个元素?链表的查找:要从链表的头指针出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止。因此,链表不是随机存取结构,按序号查找,链表的检索(查找),07:39,从第0个结点(head-next)顺链扫描,用指针p指向当前扫描到的结点,p初值p = head-next。 用count 做计数器,累计当前扫描过的结点数,

19、count 的初值为0。 当p指向扫描到的下一结点时,计数器count加1。 当count= i时,p所指的结点就是要找的第i个结点。 单链表实际长度i时,返回Null; i=-1时,返回指向头结点的指针head,【算法思想】,按序号查找,i的取值按照C/C+的数组下标编号规则,从0到n-1,头结点的编号为-1。,42,算法2.9 单链表的检索算法,template Link *InkList : setPos( int i ) /函数返回表中第 i 个元素的地址。若i =-1,返 /回head; 若 i 超出表中结点个数,则返回NULL。int count =0;if (i = -1) re

20、turn head; /i为-1定位到头结点Link *p = new Link (head-next); while ( p != NULL ,算法时间复杂度为O(n)。,单链表中的插入与删除操作,插入第一种情况:在链表最前端插入(头插法),p-next = head-next head-next = p,从一个空表开始,重复读入数据: 生成新结点 将读入数据存放到新结点的数据域中 将该新结点插入到链表的前端 直到读入结束符为止。,第二种情况:在链表末尾插入(尾插法),tail-next = p; p-next = NULL; tail=p;,每次将新结点加在插到链表的表尾; 尾指针 tai

21、l总是指向表中最后一个结点,新结点插在它的后面; 尾指针 tail初始时置为指向表头结点地址head。,第三种情况:在链表中间插入,将值为x的新结点插入到表的第i个结点的位置上,即插入到ai-1与ai之间(图2.7),(1)p-next = pre -next (2)pre-next = p,3,2,4,思考:步骤1和2能互换么?,07:39,【插入算法思想】,(1)找到ai 的前驱ai-1,用p指针指向ai-1 (2)生成一个新结点*q (3)将新结点*q的数据域置为value (4)新结点*q的指针域指向结点ai(q-next=p-next) (5)令结点*p的指针域指向新结点*q,算法2

22、.10见书P38,单链表结点的删除算法,pre-next = dele-next Delete dele,1,07:39,将单链表的第i个结点删除 算法思想: (1)找到ai-1存储位置p (2)临时保存结点ai的地址在q中,以备释放 (3)令p-next指向ai的直接后继结点 (4)将ai的值保留在e中 (5)释放ai的空间,单链表的删除算法,算法2.11见书P39,07:39,1. 查找: 因单链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为 O(n)。,2. 插入和删除: 因单链表不需要移动元素,只要修改指针,一般情况下时间复杂度为 O(1)。,但是,如果要在单链表的位置i

23、进行插入或删除操作,需要先定位查找前驱结点i-1,定位操作的平均时间代价为O(n),所以在这种情况下单链表的插入删除算法的时间复杂度为 O(n) 。,链表的运算时间效率分析,2.3.2 双链表,双链表每个结点由数据域和指针域构成,指针域包含指向后继和前驱的两个指针。,a3,a1,后继指针,前驱指针,head, 双链表结点的类定义,template class doubleLink public: T data; /结点的数据域 doubleLink * next; /结点的后继指针 doubleLink * pre; /结点的前驱 doubleLink( T info, doubleLink

24、* nextValue= NULL, doubleLink * preValue= NULL ) /具有三个参数的List构造函数 data =info;next =nextValue;pre = preValue; doubleLink(doubleLink * nextValue=NULL , doubleLink * preValue= NULL ) /具有两个参数的List构造函数next =nextValue; pre = preValue; ;,三、链表, 双链表的类定义,template class doubleList :public doubleLink /继承结点类 pri

25、vate:doubleLink * head; /定义头指针int len; /存储链表的长度 public:doubleList( )head= new doubleLink ;len=0; linkList( ) Link *p;while(head!=NULL) p=head;head=head-next;delete p; ;,三、链表,双链表结点的删除算法,p-next-pre = p-pre; P-pre-next=p-next;,1,双链表结点的删除算法,1,P-next=NULL; P-pre=NULL; Delete p;,双链表结点的插入算法(非尾结点),new q; q-

26、data=newValue; q-next = p -next; q-pre = p;P-next=q; P-next-pre=q;,1,2,4,(2)双链表结点的插入算法(尾结点),P-next=q; q-pre=p;,1,2,4,(3)循环链表结构同单链表,最后一个结点的指针指向头结点,优势:顺着任何一个结点的指针都可以找到其它结点 应用实例:可用于实现多个进程共享资源,head,(3)循环链表各类操作同单链表基本一致,差别在于判空、最后结点判断条件,head,循环链表的空表形式 head-next=head,最后一个结点的判断 p-next=head,a0,a1,an,head,循环链表

27、的应用约瑟夫问题,据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从,Josephus 要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。,约瑟夫问题算法思想,输入人数,间隔数,剩余人数,按人数创建循环链表,开始按间隔数删除链表中的结点直到剩余人数

28、,本章要求,熟练掌握顺序表和单链表的各类运算的实现; 了解双链表和循环链表的原理及相关算法。,第二章习题,开始练习,1经常需要随机查找第i个元素及其前驱的值,则采用( )存储方法节省时间A、单链表 B、双链表 C、单循环链表 D、顺序表 2 链表不具有的特点是( )。A、可随机访问任一元素 B、插入删除不需要移动元素 C、不必事前估计存储空间 D、所需空间与线性表长度成正比,第一大题:单项选题,3 线性表采用链式存储,其地址()A、必须是连续的 B、一定是不连续的 C、部分地址必须是连续的 D、连续与否都可以 4 单链表中,若*p不是尾结点,在其后插入*s的操作是( )。A、s-next=p;

29、p-next=s; B、s-next=p-next;p-next=s; C、 s-next=p-next;p=s; D、p-next=s;s-next=p,5 在一个长度为n的顺序表中,向第i个元素(0next=p B、p-next-next= p-next C、 p-next-next= pD、 p-next = p-next-next,7一个双链表中,在*p结点后插入一个结点*s的操作顺序是()。A、s-prior=p; p-next=s; p-next-prior=s; s-next=p-nextB、 p-next=s-next; p-next-prior=s; p-next=s; s-

30、prior=p;C、 p-next=s; s-prior=p;D、 p-next-prior=s; s-next=p-next; s-prior=p;p-next=s;,8 在一个双链表中,删除*p结点后的一个结点的 操作顺序是()。A、p-next= p-next-next; p-next-next -prior=pB、 p-next-prior=pp-next= p-next-next;C、 p-next= p-next-next;p-next-prior=pD、 p-next-next= p-next; p-next-prior=p,第二大题:多项选题,1 在带头结点head的单循环链表

31、中至少有一个结点的条件是( )尾结点*p的条件是。A、head-next!=NULL B、head-next!=headC、p=NULL D 、 p-next=head,2 在一个单链表的*p结点之前插入一个*s结点,可执行如下操作: S-next=( ); p-next=( ); t=p-data; p-data=( ); s-data=( ); A、t B、s C、p-next D、s-data,作业,07:39,根据单链表的头插法和尾插法的算法思想(幻灯片43,44),写出单链表的头插法和尾插法的算法,new 类型名T(初值列表) 功能: 申请用于存放T类型对象的内存空间,并依初值列表赋

32、以初值 结果值: 成功:T类型的指针,指向新分配的内存 失败:0(NULL),int *p1= new int; 或 int *p1 = new int(10);,delete 指针变量名 功能: 释放指针P所指向的内存。,delete p1;,补充:C+的动态存储分配(125页),07:39,函数调用时传送给形参表的实参必须与形参在类型、个数、顺序上保持一致参数传递有两种方式 传值方式(参数为整型、实型、字符型等) 传地址 参数为指针变量 参数为引用类型 参数为数组名,补充:C+中的参数传递,07:39,void main() float a,b;cinab;swap(a,b); couta

33、endlbendl; ,#include void swap(float m,float n) float temp;temp=m;m=n;n=temp; ,传值方式,把实参的值传送给函数局部工作区相应的副本中,函数使用这个副本执行必要的功能。函数修改的是副本的值,实参的值不变,07:39,传地址方式指针变量作参数,void main() float a,b,*p1,*p2;cinab;p1= ,#include void swap(float *m,float *n) float t;t=*m;*m=*n;*n=t; ,形参变化影响实参,07:39,传地址方式引用类型作参数(137页),引用

34、:它用来给一个对象提供一个替代的名字。,#include void main()int i=5;int ,j是一个引用类型, 代表i的一个替代名 i值改变时,j值也跟着改变,所以会输出 i=7 j=7,什么是引用?,07:39,void main() float a,b;cinab;swap(a,b); coutaendlbendl; ,#include void swap(float m,float n) float temp;temp=m;m=n;n=temp; ,传地址方式引用类型作参数,07:39,(1)传递引用给函数与传递指针的效果是一样的,形参变化实参也发生变化。 (2)引用类型作形参,在内存中并没有产生实参的副本,它直接对实参操作;引用不占用新的地址,当参数传递的数据量较大时,用引用比用一般变量传递参数的时间和空间效率都好。,引用类型作形参的三点说明,

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

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

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


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

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

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