1 / 68

第二章 线性表

第二章 线性表. 2.1 线性表及其基本运算 2.2 线性表的顺序存储结构 2.3 线性表的链式存储结构 线性表的逻辑结构、物理结构,以及它们之间的相互关系; 定义与之相适应的运算; 设计相应的算法; 分析算法的效率。. 线性表. 2.1线性表的逻辑结构. 线性表是最简单、也是最基本的一种线性数据结构。它有两种存 储表示方法:顺序表(数组)和链表,它的主要操作是插入、删 除和查找等。. 下面看几个线性表的的例子: ①、一副扑克牌中同一种花色的13 张牌排列起来可以看成是一个

mairi
Download Presentation

第二章 线性表

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第二章 线性表 2.1 线性表及其基本运算 2.2 线性表的顺序存储结构 2.3 线性表的链式存储结构 • 线性表的逻辑结构、物理结构,以及它们之间的相互关系; • 定义与之相适应的运算; • 设计相应的算法; • 分析算法的效率。

  2. 线性表 2.1线性表的逻辑结构 线性表是最简单、也是最基本的一种线性数据结构。它有两种存 储表示方法:顺序表(数组)和链表,它的主要操作是插入、删 除和查找等。 下面看几个线性表的的例子: ①、一副扑克牌中同一种花色的13 张牌排列起来可以看成是一个 线性表(A,2,3,4,5,6,7,8,9,10,J,Q,K),其中每一张牌是一个数据元素. ②、人民币面值的种类归结起来也是一个线性表(1分,2分, 5分,1角,2角,5角,1元,2元,5元,10元,50元,100元), 其中每一种面值是一个数据元素 ③、如在学生学籍档案线性表中,每个学生的档案是一个数据元 素,它由学号、姓名和各项成绩组成,如图:

  3. 表2.1 学生的学籍档案表 ... 学号 入学总分 数学分析 程序设计 姓名 435 88 ... 王国强 981201 65 ... 85 90 429 赵纪实 981202 ... 97 88 刘晔 981203 512 ... 91 488 93 叶桑林 981204 ... ... ... ... ... ... ... 田华明 981350 501 89 95 在上表中,一个数据元素由若干个数据项组成,数据元素称为记 录,含有大量记录的线性表称为文件

  4. 2.1 线性表及其基本运算 一、线性表(linear_list) 线性表是n个数据元素的有限序列,记为: L=(a1,a2, …,an) 数据元素之间的关系是: ai-1领先于ai,ai领先于ai+1。 称ai-1是ai的直接前驱 元素;ai+1是ai的直接后继 元素。 除a1外,每个元素有且仅有一个直接前驱元素, 除an外,每个元素有且仅有一个直接后继元素。 线性表中数据元素的个数n(n>=0)称为线性表的长度, 当n=0时,称为空表。

  5. 2.1 线性表及其基本运算 线性表是最常用且最简单的一种数据结构, 它的形式化定义为: linear_list=(D,R) 其中,D={ai| ai ∈DO,i=1,2,...,n,n>=0} R={N},N={<ai-1, ai>|ai-1, ai∈D0,i=2,3,...,n} D0为某个数据对象, N是一个序偶的集合,它表示线性表中数据元素之间的相邻关系。

  6. 2.1 线性表及其基本运算 二. 基本运算 INITIATE(L)初始化操作 设定一个空的线性表L LENGTH(L)求长度函数 值为L中数 据元素的个数 GET(L,i)取元素函数 1<=i<=LENGTH(L)时返回L中第i个数据元素,否则为空元素NULL。 i称为该数据元素在L中的位序 PRIOR(L,elm)求前驱函数elm为L中的一个数据元素,若它的位序大于1,则函数值为elm前驱,否则为NULL NEXT(L,elm)求后继函数 若elm的位序小于表长, 则函数值为elm的后继,否则为NULL

  7. 2.1 线性表及其基本运算 LOCATE(L,x)定位函数 给定值x,若x不在表中,则返回0,否则,返回x在表中第一次出现时的位序 INSERTE(L, i , b)前插操作 在第i个元素之前插入新元素b,i 的取值范围为:1<=i<=n+1;i =n+1表示在表尾插入, n为表长 DELETE(L,i)删除操作 删除线性表L中的第i个元素, 1<=i<=n EMPTY(L)判空表函数 若L为空表,则返回布尔值“true”,否则返回布尔值“false” CLEAR(L)表置空操作 将L置为空表

  8. 2.1 线性表及其基本运算 例2-1求两个集合的并,即A=A∪B 分析:设A、B分别由两个线性表LA和LB表示, 要求: 将LB中存在而LA中不存在的DE插入到表LA中。 算法思想:① 依次从LB中取出一个DE; ② 判在LA中是否存在; ③ 若不存在,则插入到LA中。

  9. 2.1 线性表及其基本运算 形式化算法描述(类PASCAL): PROC union(VAR LA:Linear_list; LB:Linear_list); {将所有在LB中存在而LA中不存在的DE插入到LA中去} n:=LENGTH(LA); {确定线性表LA的长度} FOR i:=1 TO LENGTH(LB) DO [ x:=GET(LB,i); {取LB中第i个数据元素} k:=LOCATE(LA,x); {在LA中进行搜索} IF k=0 THEN [ INSERT(LA,n+1,x);{在LA表尾插入} n:=n+1 {表长加1} ] ] ENDP; {union}

  10. 2.1 线性表及其基本运算 例2-2归并两个有序的线性表LA和LB为一个新的有序线性表LC。 算法思想: ① 初始化:置LC为空表, 设置变量i,j,初值为1,分别指向LA和LB的第一个DE,k初值为0,表示LC的长度。 ② 当i<=LENGTH(LA) AND j<=LENGTH(LB)时, 判断:若i所指的元素<=j所指的元素,则将i所指的元素插入在LC的k+1前,并且i,k的值分别加1; 否则,将j所指的元素插入在LC的k+1前,并且j,k的值分别加1。 ③ 重复②直到某个表的元素插入完毕。 ④ 将未插入完的表的余下的元素,依次插入在LC后。

  11. 2.1 线性表及其基本运算 PROC merge_list(LA,LB:linear_list; VAR LC:Linear_list); {LA和LB中元素依值非递减有序排列,归并得到的LC中的元 素仍依值非递减有序排列} INITIATE(LC);i:=1; j:=1; k:=0; {初始化} WHILE (i<=LENGTH(LA)) AND (j<=LENGTH(LB)) DO IF GET(LA,i)<=GET(LB,J) THEN [ INSERT(LC, k+1,GET(LA,i)); k:=k+1; i:=i+1] ELSE [ INSERT(LC, k+1,GET(LB,j)); k:=k+1; j:=j+1]; WHILE i<=LENGTH(LA) DO [ INSERT(LC,k+1,GET(LA,i)); k:=k+1; i:=i+1]; WHILE j<=LENGTH(LB) DO [ INSERT(LC,k+1,GET(LB,j)); k:=k+1; j:=j+1] ENDP; {merge_list}

  12. I J 3 5 8 11 2 6 8 9 11 15 20 LA LB K 2 3 5 6 8 9 11 11 15 20 8 LC 算法分析:该算法中包含了三个WHILE语句,其中,第一个处理了某一张表的全部元素和另一张表的部分元素;后两个WHILE循环只可能有一个执行,用来完成将未归并到LC中的余下部分元素插入到LC中。“插入”是估量归并算法时间复杂度的基本操作,其语句频度为: LENGTH(LA)+LENGTH(LB)

  13. 2.1 线性表及其基本运算 该算法的时间复杂度为: O(LENGTH(LA)+LENGTH(LB)),若LA和LB的元素个数为同数量级n,则该算法的时间复杂度为O(n)。 作业:已知一个非纯集合B(即集合B中可能有相同的元素),试 构造一个纯集合A,使A中只包含B中所有值各不相同的成员。 (请用上面的函数写出算法)

  14. 作业:已知一个非纯集合B(即集合B中可能有相同的元素),试构造一个纯集合A,使A中只包含B中所有值各不相同的成员。(请用上面的函数写出算法 p20) PROC purge(VAR La:Linear_list; Lb:Linear_list); Initiate(La); La_len=0; Lb_len=length(Lb); For I:=1 to Lb_len Do [ x=get(Lb,I); k:=Locate(La,x); IF k=0 Then [Insert(La,La_len+1,x); La_len:=La_len+1] ] ENDP

  15. a1 a2 ai an b b+k b+(i-1)k b+(n-1)k 2.2 线性表的顺序存储结构 一、顺序存储结构 用一组地址连续的存储单元依次存储线性表的元素。设线性表的每个元素占用k个存储单元,则第i个元素ai 的存储位置为:Loc(ai)=Loc(a1)+(i-1)*k 其中,Loc(a1)为线性表的起址。

  16. 2.2 线性表的顺序存储结构 线性表顺序存储结构的定义为: CONST maxlen=线性表可能达到的最大长度; TYPE sqlisttp=RECORD elem:ARRAY[1..maxlen] OF elemtp; last:0..maxlen END 在上述描述中,线性表的顺序存储结构是一个记录型的结构。 其中,数据域 elem描述了线性表中的DE占用的数组空间, 数组的第i个分量为线性表中第i 个DE的存储映象; 数据域last指示最后一个DE在数组空间中的位置,也是表长。

  17. 2.2 线性表的顺序存储结构 二. 插入和删除操作 1. 插入运算 INSERT(L, i, b) • 插入前:L=(a1, ... , ai-1, ai, ... ,an) • 插入后:L=(a1, ... , ai-1, b, ai, ... ,an ) 算法思想: ① 进行合法性检查,1<=i<=n+1; ② 判断线性表是否已满; ③ 将第n个至第i个元素逐一后移一个单元; ④ 在第i个位置处插入新元素; ⑤ 将表的长度加1。

  18. 25 线性表的插入操作:INSERT(L,I,b) 12 13 21 24 28 30 42 77 12 13 21 24 25 28 30 42 77

  19. 2.2 线性表的顺序存储结构 PROC ins_sqlist(VAR v:sqlisttp;i:integer;b:elemtp); {在顺序存储结构的线性表v中第i个DE之前插入b} IF (i<1) OR (i> v.last+1) THEN ERROR(‘i值不合法’) ELSE IF v.last >= maxlen THEN ERROR(‘表满溢出’) ELSE [ FOR j:=v.last DOWNTO i DO v.elem[j+1]:=v.elem[j]; {右移} v.elem[i]:=b; v.last:=v.last+1 ] ENDP;{ins_sqlist}

  20. 2.2 线性表的顺序存储结构 插入算法时间复杂度分析: 最坏情况是在第1个元素前插入(i=1), 此时,要后移n个元素,因此, T(n)=O(n)

  21. 2.2 线性表的顺序存储结构 2. 删除运算DELETE(L,i) • 删除前:L=(a1,...,ai-1,ai,ai+1,...,an) • 删除后:L=(a1,...,ai-1,ai+1,...,an) 算法思想: ① 进行合法性检查,i是否满足1<=i<=n; ② 判线性表是否已空,v.last=0; ③ 将第i+1至第n个元素逐一向前移一个位置; ④ 将表的长度减1。

  22. 删除24 线性表的删除操作:DELETE(L,I) 12 13 21 24 25 28 30 42 77 12 13 21 24 25 28 30 42 77

  23. 2.2 线性表的顺序存储结构 形式化算法描述: PROC del_sqlist(VAR v:sqlisttp; i:integer); {在顺序存储结构的线性表v中删除第i个DE} IF (i<1) OR (i>v.last) THEN ERROR(‘i值有错或表已空’) ELSE [ FOR j:=i+1 TO v.last DO v.elem[j-1]:=v.elem[j]; v.last:=v.last-1] ENDP; {del_sqlist} 时间复杂度分析:最坏情况是删除第1个元素, 此时要前移n-1个元素,因此:T(n)=O(n)

  24. 2.2 线性表的顺序存储结构 三. 线性表顺序存储结构的特点 (1).逻辑上相邻的元素,其物理位置也相邻; (2). 可随机存取表中任一元素; (3). 必须按最大可能长度预分存储空间,存储空间利用率低,表的容量难以扩充,是一种静态存储结构; (4). 插入删除时,需移动大量元素,平均移动元素为n/2。

  25. 2.2 线性表的顺序存储结构 例2-3 顺序结构上的归并有序表算法 PROC merge_sqlist(va,vb:sqlisttp;VAR vc:sqlisttp); i:=1; j:=1; k:=0; WHILE (i<=va.last) AND (j<=vb.last)) DO IF va.elem[i]<=vb.elem[j] THEN [vc.elem[k+1]:=va.elem[i]; k:=k+1; i:=i+1] ELSE [vc.elem[k+1]:=vb.elem[j]; k:=k+1;j:=j+1]; WHILE i<=va.last DO [ vc.elem[k+1]:=va.elem[i]; k:=k+1; i:=i+1]; WHILE j<=vb.last DO [ vc.elem[k+1]:=vb.elem[j]; k:=k+1; j:=j+1]; vc.last:=k ENDP; {merge_list}

  26. 算法时间复杂度为O(va.last+vb.last) 结论:线性表在进行归并前,应先对表中元素进行排序 思考题:设有一个用向量表示的线性表L,要求写出 一个将该表逆置的过程,并只允许在原表的存储空 间外在增加一个附加的工作单元。 Procedure converts( var L:seqlist); Begin k:=L.length; m:=k div 2; for I:=1 to m do begin x:=L.element[I]; L.element[I]:=L.element[k-I+1];

  27. L.element[k-I+1]:=x end End; 讨论:这个算法过程只须进行数据元素的交换操作,其主要时间 花费在for循环上,整个算法的时间复杂度为O(k) 作业:设A=(a1,a2,…am)和B=(b1,b2,…bn)均为顺序表,A’和B’分 别为A和B中除去最大共同前缀后的子表(例如,A=(x,y,y,z,x,z), B=(x,y,y,z,y,x,x,z),则两者中最大的共同前缀为(x,y,y,z),在两表中 除去共同前缀后的子表分别为A’=(x,z),B’=(y,x,xz)。 若A’=B’=空表,则A=B。若A’=空表,而B’≠空表,或者两者均 不为空表,且A’的首元素小于B’的首元素,则A<B;否则A>B。 试写一个比较A、B大小的算法。

  28. Int compare(Sqlist A, Sqlist B) { j:=1; while(j<=A.length && j<=B.length){ if (A.elem[j]< B.elem[j]) return (-1); else if (A.elem[j]>B.elem[j]) return (1) else j++; if (A.length==B.length) return(0); else if (A.length<B.length) return(-1); else return(1); }

  29. 一. 线性链表 1. 链式存储结构 用一组任意的存储单元(不要求地址连续)来存储线性表的元素,每个元素对应一组存储单元(称为结点),每个结点包括两个域:存储数据元素信息的数据域 和存储直接后继所在位置的指针域。 N个结点通过指针域组成的表,称为线性链表(单链表)。 • 线性链表最后一个结点的指针域为“空”(NIL或∧); • 用一个头指针指示链表中第一个结点的存储位置; • 链表L=(a1,a2,...,an)逻辑表示为: L 空表时:L=NIL an NIL a1 a2 2.3 线性表的链式存储结构

  30. 2.3 线性表的链式存储结构 用Pascal的指针类型定义单链表 TYPE pointer=↑nodetype; nodetype=RECORD data:elemtp; next:pointer END; linkisttp=pointer; • pointer为一指针,指向nodetype记录类型; • nodetype为一记录,它由data和next两项组成; • data为数据元素类型,next为pointer指针; • 单链表linkisttp定义为pointer指针。

  31. 2.3 线性表的链式存储结构 2. 带头结点的线性链表 在线性链表的第一个元素结点之前附设一个结点(称头结点),它的数据域不存储任何信息,其指针域存储第一个元素结点的存储位置。头指针L指向该头结点。 空表时:L↑.next=NIL 带头结点链表的引入是为了使算法判空和处理一致。

  32. 头结点和头指针的区别: 头结点是在第一个结点前附设的一个结点,包含数据域和指 针域。头指针指向第一个结点或头结点。 H ∧ qian sun li 头结点 头指针 设置头结点的好处: 1、由于第一个元素结点的位置存放在结点的指针域中, 所以在链表的第一个位置上的操作就和在表的其他位置上的操 作一致。 2、无论链表是否为空,其头指针是指向头结点的非空指 针,因此空表和非空表的处理也统一

  33. 指针的基本操作示例 操作内容 执行操作的语句 指针指向结点 p=q zhao qian sun 执行前 q 执行后 zhao qian sun p q

  34. 操作内容 执行操作的语句 指针指向后继 p=q↑.next zhao qian sun 执行前 q 执行后 zhao qian sun p q

  35. 操作内容 执行操作的语句 指针移动 p=p↑.next zhao qian sun 执行前 p 执行后 zhao qian sun p

  36. 操作内容 执行操作的语句 链指针改接 p↑.next=q zhao qian sun 执行前 p qian sun q zhao qian sun 执行后 p qian sun q

  37. 操作内容 执行操作的语句 链指针改接 后继 p↑.next=q↑.next zhao qian sun 执行前 p qian sun q zhao qian sun p 执行后 qian sun q

  38. 2.3 线性表的链式存储结构 3. 几种基本运算在单链表上的实现 (1)GET(L,i)函数 FUNC get_linklist(la:linkisttp;i:integer):elemtp; [置初值部分] WHILE (循环控制条件)DO [ 循环体] IF (出循环后判断条件) THEN RETURN(p↑.data) ELSE RETURN(NULL) ENDF;{get_linklist}

  39. 2.3 线性表的链式存储结构 问题:如何置初值?怎样对i的合法性进行检查? 如何确定出循环的判定条件? FUNC get_linklist(la:linkisttp;i:integer):elemtp; p:=la↑.next;j:=1;{移动指针p,计数变量j} WHILE (p<>NIL AND j<i) DO {条件1防止i>表长} [ p:=p ↑.next;j:=j+1;]{条件2控制取第i个,防i<1} IF (j=i)            IF (p=NIL) THEN RETURN(p↑.data) 或 THEN RETURN(NULL) ELSE RETURN(NULL) ELSE RETURN(p↑.data) ENDF;{get_linklist}

  40. p la ∧ qian sun li 头结点 头指针

  41. 2.3 线性表的链式存储结构 循环条件分析: p:=la↑.next;j:=1; WHILE (p<>NIL AND j<i) DO [ p:=p ↑.next;j:=j+1;] 条件1防止i>表长,条件2控制取第i个,并防止了i<1. 两个条件有6种组合: 1.P=NIL AND j<i 空表且i>1 或i>表长+1,异常,返回NULL 2.P=NIL AND j=i 空表且i=1 或i=表长+1 ,异常,返回NULL 3.P=NIL AND j>i 空表且i<1 ,异常,返回NULL 4.P<>NIL AND j<i 继续循环 5. P<>NIL AND j=i 确定第i个结点 ,正常出循环 6. P<>NIL AND j>i i<1 ,异常,返回NULL

  42. 2.3 线性表的链式存储结构 FUNC get_linklist(la:linkisttp;i:integer):elemtp; p:=la↑.next;j:=1;{移动指针p,计数变量} WHILE (p<>NIL AND j<i) DO {条件1防止i>表长} [ p:=p ↑.next;j:=j+1;]{条件2控制取第i个,防i<1} IF (p<>NIL AND j=i)   {只用一个条件是不充分的} THEN RETURN(p↑.data) {正常出循环} ELSE RETURN(NULL)  {异常出循环} ENDF;{get_linklist} • i的合法性检查已经蕴含在WHILE和IF条件中; •  算法时间复杂度分析:WHILE 最多执行i-1次,最坏情况是取第n个结点,需执行n-1次,故:T(n)=O(n)

  43. 2.3 线性表的链式存储结构 (2).INSERT(L,i,b)插入运算 设在单链表结点x和结点y之间插入新结点b,已知p为指向结点x的指针,s为指向新结点b的指针。 插入前: 插入后: 插入运算可以由定位和修改指针来完成: 定位:得到指向y的前驱的指针p; 修改指针:s↑.next:=p↑.next; p↑.next:=s; {顺序不可换} p p x y x y s s b b

  44. 2.3 线性表的链式存储结构 PROC ins_linklist(la:linkisttp;i:integer;b:elemtp); {la为带头结点单链表的头指针} p:=la;j:=0;{置初值} WHILE (p<>NIL AND j<i-1) DO {定位} [ p:=p↑.next;j:=j+1;] IF (P=NIL OR j>i-1) THEN ERROR(“插入位置不对”) ELSE [ new(s); s ↑.data:=b; {插入} s ↑.next :=p ↑.next ; p ↑.next :=s] ENDP;{ins_linklist}

  45. 与get_linklist函数的区别:   初值保证能在第一个DE之前插入, 插入范围为[1,表长+1]   循环定位条件,定位在i的直接前驱i-1 出循环IF条件是get_linklist的求反,定位后,插入 • 循环条件分析: 第一次循环p<>NIL,j有三种可能:① j<i-1 继续循环; ② j=i-1 此时i=1,在第一个元素前插入;③ j>i-1 i<1不合法 第二次循环后p可能为NIL,但j>i-1不可能,出现的各种可能情况: ① p<>NIL AND j<i-1 继续循环; ② p<>NIL AND j=i-1 已经定位,出循环 ③  p=NIL AND j<=i-1 i不合法(i>=表长+2),出循环 • 算法时间复杂度:关键在定位,最坏情况是INSERT(L,n+1,b)   WHILE 执行n次,故 T(n)=O(n)

  46. 2.3 线性表的链式存储结构 (3) DELETE(L,i)删除运算 p 删除前 删除后 删除运算可以由定位和修改指针来完成: • 定位:得到指向第i个元素的前驱的指针p; • 修改指针:p↑.next:=p↑.next ↑.next ; p

  47. 2.3 线性表的链式存储结构 PROC del_linklist(la:linkisttp;i:integer); {la为带头结点单链表的头指针} p:=la;j:=0; WHILE (p ↑.next <>NIL AND j<i-1) DO [ p:= p↑.next;j:=j+1;] IF (p ↑.next=NIL OR j>i-1) THEN ERROR(“表空或位置不对”) ELSE [ q:=p ↑.next; p ↑.next:=p ↑.next ↑.next ; dispose(q) ] ENDP;{del_linklist}

  48. 2.3 线性表的链式存储结构 删除算法讨论: • 删除范围为[1,表长],不能删除头结点; • 出循环的五种可能情况: p ↑.next=NIL AND j<i-1 空表或i>表长 p ↑.next=NIL AND j=i-1 空表且i=1,或i=表长+1 p ↑.next=NIL AND j>i-1 空表且i<1 p ↑.next<>NIL AND j=i-1 非空表且已定位 p ↑.next<>NIL AND j>i-1 非空表且i<1 • 时间复杂度:WHILE 至多执行i-1次,当i=n时为最坏情形, 因此,T(n)=O(n)

  49. 思考题:设计一个算法,求出线性链表中数据域值为x的节点序号,该序号应从链表的首节点算起,若链表中无此节点则序号为零。思考题:设计一个算法,求出线性链表中数据域值为x的节点序号,该序号应从链表的首节点算起,若链表中无此节点则序号为零。 Function index(H:linklist):integer; begin p:=H; j:=1; while(p↑.next<>nil) and (p↑.data<>x) do begin p:=p ↑.next; j:=j+1 end; if p↑.next=nil then index:=0 else index:=j end; 函数的时间复杂度为O(n)

  50. 2.3 线性表的链式存储结构 (4)建立链表算法 • 算法思想:从空链表开始,依次插入各个结点。 PROC crt_linklist(VAR la:linkisttp;a:ARRAY[1..n] OF elemtp); new(la);la↑.next=NIL ; {建立空表,只有一个头结点} FOR i:=n DOWNTO 1 DO [ new(p); p↑.data=a[i] ; {等价于INSERT(L,1,a[i])} p↑.next=la↑.next;la↑.next=p ] ENDP;{crt_linklist} • 思考:为什么从n到1依次插入?若从1到n算法将如何实现? • 算法时间复杂度:T(n)=O(n)

More Related