数据结构课件(第二章).ppt_第1页
数据结构课件(第二章).ppt_第2页
数据结构课件(第二章).ppt_第3页
数据结构课件(第二章).ppt_第4页
数据结构课件(第二章).ppt_第5页
已阅读5页,还剩65页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第二章 链表,西安工程大学 计算机科学学院,第2章 线性表,2.1 线性表的概念及运算 2.2 线性表的顺序存储 2.3 线性表的链式存储 2.4 一元多项式的表示及相加,2.1 线性表的概念及运算,2.1.1 线性表的逻辑结构 2.1.2 线性表的抽象数据类型定义,return,线性表的定义,线性表(Linear List)是由n (n0)个类型相同的数据元素a1,a2,,an组成的有限序列,记做(a1,a2,,ai-1,ai,ai+1, ,an)。 数据元素之间是一对一的关系,即每个数据元素最多有一个直接前驱和一个直接后继。 线性表的逻辑结构图为:,线性表的特点,同一性:线性表由同类数据元

2、素组成,每一个ai必须属于同一数据对象。 有穷性:线性表由有限个数据元素组成,表长度就是表中数据元素的个数。 有序性:线性表中相邻数据元素之间存在着序偶关系。,return,2.1.2 线性表的抽象数据类型定义,ADT LinearList 数据元素:D=ai| aiD0, i=1,2,,n 0 ,D0为某一数据对象 关系: | ai, ai+1D0,i=1,2, ,n-1 基本操作: (1)InitList(L) 操作前提:L为未初始化线性表。 操作结果:将L初始化为空表。 (2)DestroyList(L) 操作前提:线性表L已存在。 操作结果:将L销毁。 (3)ClearList(L)

3、操作前提:线性表L已存在 。 操作结果:将表L置为空表。 ADT LinearList,return,2.2 线性表的顺序存储,2.2.1 线性表的顺序存储结构 2.2.2 线性表顺序存储结构上的基本运算,return,顺序存储结构的定义,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。采用顺序存储结构的线性表通常称为顺序表。 假设线性表中每个元素占k个单元,第一个元素的地址为loc(a1),则第k个元素的地址为: loc(ai) =lo

4、c(a1)+(i-1)k,顺序存储结构示意图,空闲,顺序存储结构的C语言定义,#definemaxsize=线性表可能达到的最大长度; typedef struct ElemType elemmaxsize; /* 线性表占用的数组空间*/ int last; /*记录线性表中最后一个元素在数组 elem 中的位置(下标值),空表置为-1*/ SeqList; 注意区分元素的序号和数组的下标,如a1的序号为1,而其对应的数组下标为0。,return,2.2.2 线性表顺序存储结构的基本运算,线性表的基本运算: 查找操作 插入操作 删除操作 顺序表合并算法 线性表顺序存储结构的优缺点分析,ret

5、urn,SeqList *L;,SeqList L;,查找操作,按序号查找GetData(L,i):查找线性表L中第i个数据元素,其结果是L.elemi-1或L-elemi-1。 按内容查找Locate(L,e): 查找线性表L中与给定值e相等的数据元素,若在表L中找到与e相等的元素,则返回该元素在表中的序号;若找不到,则返回一个“空序号”,如-1。,线性表的查找运算算法:,int Locate(SeqList L,ElemType e) i=0 ; / *i为扫描计数器,初值为0,即从第一个元素开始比较*/ while (i=L.last) /*若没找到,则返回空序号*/ ,return,插

6、入操作,线性表的插入运算是指在表的第i (1in+1)个位置,插入一个新元素e,使长度为n的线性表 (e1,ei-1,ei,en) 变成长度为n+1的线性表 (e1,,ei-1,e,ei,en)。,插入算法示意图,已知线性表(4,9,15,28,30,30,42,51,62),需要在第4个元素之前插入元素“21”。算法示意图如下:,1,2,3,4,5,6,7,8,9,10,序号,4,9,15,28,30,30,42,51,62,4,9,15,28,28,30,30,42,51,62,4,9,15,21,28,30,30,42,51,62,移动元素,插入元素,插入运算算法:,int InsLis

7、t(SeqList *L,int i,ElemType e) int k; if( (iL-last+2) ) /*首先判断插入位置是否合法*/ printf(“插入位置i值不合法”); return(ERROR); if(L-last=maxsize-1) printf(“表已满无法插入”); return(ERROR); for(k=L-last;k=i-1;k-) /*为插入元素而移动位置*/ L-elemk+1=L-elemk; L-elemi-1=e; /*在C语言中数组第i个元素的下标为i-1*/ L-last+; return(OK); ,return,删除操作,线性表的删除运算

8、是指将表的第i(1in)个元素删去,使长度为n的线性表 (e1,,ei-1,ei,ei+1,en), 变成长度为n-1的线性表 (e1,,ei-1, ei+1,en)。,删除算法示意,将线性表 (4,9,15,21,28,30,30,42,51,62) 中的第5个元素删除。,序号,删除28后,删除算法描述:,int DelList(SeqList *L,int i,ElemType *e) /*在顺序表L中删除第i个数据元素,并用指针参数e返回其值*/ int k; if(iL-last+1) printf(“删除位置不合法!”); return(ERROR); *e= L-elemi-1;

9、/* 将删除的元素存放到e所指向的变量中*/ for(k=i;klast;k+) L-elemk-1= L-elemk; /*将后面的元素依次前移*/ L-last-; return(OK); ,return,合并算法,已知:有两个顺序表LA和LB,其元素均为非递减有序排列,编写一个算法,将它们合并成一个顺序表LC,要求LC也是非递减有序排列。 算法思想 :设表LC是一个空表,为使LC也是非递减有序排列,可设两个指针i、j分别指向表LA和LB中的元素,若LA.elemiLB.elemj,则当前先将LB.elemj插入到表LC中,若LA.elemiLB.elemj ,当前先将LA.elemi插入

10、到表LC中,如此进行下去,直到其中一个表被扫描完毕,然后再将未扫描完的表中剩余的所有元素放到表LC中。,顺序表合并算法实现,void merge(SeqList *LA, SeqList *LB, SeqList *LC) i=0;j=0;k=0; while(ilast ,return,顺序存储结构的优点和缺点,优点: 1.无需为表示结点间的逻辑关系而增加额外的存储空间; 2.可方便地随机存取表中的任一元素。 缺点: 1.插入或删除运算不方便,除表尾的位置外,在表的其它位置上进行插入或删除操作都必须移动大量的结点,其效率较低; 2.由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分

11、配。因此当表长变化较大时,难以确定合适的存储规模。,return,2.3 线性表的链式存储,链表:采用链式存储结构的线性表称为链表 。 现在我们从两个角度来讨论链表: 1.从实现角度看,链表可分为动态链表和静态链表。 2.从链接方式的角度看,链表可分为单链表、循环链表和双链表。,链表,2.3.1 单链表 2.3.2 单链表上的基本运算 2.3.3 循环链表 2.3.4 双向链表 2.3.5 静态链表 2.3.6 顺序表和链表的比较,return,2.3.1 单链表,结点(Node)为了正确地表示结点间的逻辑关系,必须在存储线性表的每个数据元素值的同时,存储指示其后继结点的地址(或位置)信息,这

12、两部分信息组成的存储映象叫做结点(Node)。 单链表:链表中的每个结点只有一个指针域,将这种链表称为单链表。单链表包括两个域:数据域用来存储结点的值;指针域用来存储数据元素的直接后继的地址(或位置)。 头指针 :指向链表头结点的指针。,单链表示意图,31,头指针H,带头结点的单链表示意图:,带头结点的空单链表 带头结点的单链表,H,H,单链表的存储结构描述,typedef struct Node / * 结点类型定义 * / ElemType data; struct Node * next; Node, *LinkList; /* LinkList为结构指针类型*/,return,2.3.

13、2 单链表上的基本运算,线性表的基本运算: 建立单链表 单链表查找 单链表插入操作 单链表删除 算法应用示例: 求单链表的长度 求两个集合的差,return,建立单链表,头插法 从一个空链表开始,将新生成的结点插入到当前链表的表头结点之后。 尾插法 从一个空链表开始,将新生成的结点插入到当前链表的表尾结点之后。,建立单链表,头插法建立单链表 算法描述:从一个空表开始,重复读入数据,生成新结点,将读入的数据放入新结点的数据域中,然后将新结点插入到当前链表的头结点之后,直到读入结束标志为止。,H,c1,s,c2,s,头插法建表算法,Linklist CreateFromHead() LinkLis

14、t L; Node *s; int flag=1; /* 设置一个标志,初值为1,当输入“$”时,flag为0,建表结束*/ L=(Linklist)malloc(sizeof(Node);/*为头结点分配存储空间*/ L-next=NULL; while(flag) c=getchar(); if(c!=$) /*为读入的字符分配存储空间*/ s=(Node*)malloc(sizeof(Node); s-data=c; s-next=L-next; L-next=s; else flag=0; ,尾插法建立链表示意,H,c1,s,c2,s,尾插法建表算法,Linklist CreateFr

15、omTail() LinkList L; Node *r, *s; int flag =1; L=(Node * )malloc(sizeof(Node); /*为头结点分配存储空间*/ L-next=NULL; r=L; /*r指针始终动态指向链表的当前表尾*/ while(flag)/*标志,初值为1。输入“$”时flag为0,建表结束*/ c=getchar(); if(c!=$) s=(Node*)malloc(sizeof(Node); s-data=c; r-next=s; r=s; else flag=0; r-next=NULL; ,return,单链表查找,按序号查找 算法描

16、述:设带头结点的单链表的长度为n,要查找表中第i个结点,则需要从单链表的头指针L出发,从头结点(L-next)开始顺着链域扫描,用指针p指向当前扫描到的结点,初值指向头结点(p=L),用j做记数器,累计当前扫描过的结点数(初值为0),当j = i 时,指针p所指的结点就是要找的第i个结点 。,按序号查找算法实现,/ * 在带头结点的单链表L中查找第i个结点,若找到(1in),则返回该结点的存储位置; 否则返回NULL * / Node * Get(LinkList L, int i) Node *p; p=L;j=0; / * 从头结点开始扫描 * / while (p-next!=NULL)

17、 p=L-next; / * 从表中第一个结点比较 * / while (p!=NULL) if (p-data!=key) p=p-next; else break; / * 找到结点key,退出循环 * / return p; ,return,单链表的插入操作,算法描述:要在带头结点的单链表L中第i个数据元素之前插入一个数据元素e,需要首先在单链表中找到第i-1个结点并由指针pre指示,然后申请一个新的结点并由指针s指示,其数据域的值为e,并修改第i-1个结点的指针使其指向s,然后使s结点的指针域指向第i个结点。,H,a1,ai-1,an,ai,e,s,pre,单链表插入操作算法实现,vo

18、id InsList(LinkList L,int i,ElemType e) /*在带头结点的单链表L中第i个结点之前插入值为e的新结点。 */ Node *pre,*s; int k=0; pre=L; while(pre-next!=NULL ,return,单链表的删除操作,算法描述:欲在带头结点的单链表L中删除第i个结点,则首先要通过计数方式找到第i-1个结点并使p指向第i-1个结点,而后删除第i个结点并释放结点空间,H,a1,ai-1,an,ai,ai+1,p,r,单链表删除算法实现,void DelList(LinkList L,int i,ElemType *e) /*在带头结

19、点的单链表L中删除第i个元素,并保存其值到变量*e中*/ Node *p,*r; p=L; int k =0; while(p-next!=NULL /*释放被删除的结点所占的内存空间*/ ,return,求单链表的长度,算法描述:可以采用“数”结点的方法来求出单链表的长度,用指针p依次指向各个结点,从第一个元素开始“数”,一直“数”到最后一个结点(p-next=NULL)。 int ListLength(LinkList L) Node *p; p=L-next; j=0; /*用来存放单链表的长度*/ while(p!=NULL) j+; p=p-next; return j; ,retu

20、rn,求两个集合的差,已知:以单链表表示集合,假设集合A用单链表LA表示,集合B用单链表LB表示,设计算法求两个集合的差,即A-B。 算法思想:由集合运算的规则可知,集合的差A-B中包含所有属于集合A而不属于集合B的元素。具体做法是,对于集合A中的每个元素e,在集合B的链表LB中进行查找,若存在与e相同的元素,则从LA中将其删除。,求两个集合的差算法实现,void Difference (LinkList LA,LinkList LB) Node *pre;*p, *r; pre=LA;p=LA-next; /*p向表A中的某一结点,pre始终指向p的前驱*/ while(p!=NULL) q

21、=LB-next; /*扫描LB中的结点,寻找与LA中*P结点相同的结点*/ while (q!=NULL ,return,2.3.3 循环链表,循环链表(Circular Linked List) 是一个首尾相接的链表。 特点:将单链表最后一个结点的指针域由NULL改为指向头结点或线性表中的第一个结点,就得到了单链形式的循环链表,并称为循环单链表。在循环单链表中,表中所有结点被链在一个环上。,带头结点的循环单链表示意图,L,L,rear,*rear,*(rear-next),循环单链表合并为一个循环单链表,已知:有两个带头结点的循环单链表LA、LB,编写一个算法,将两个循环单链表合并为一个循

22、环单链表,其头指针为LA。 算法思想:先找到两个链表的尾,并分别由指针p、q指向它们,然后将第一个链表的尾与第二个表的第一个结点链接起来,并修改第二个表的尾Q,使它的链域指向第一个表的头结点。,循环单链表合并算法实现,LinkList merge_1(LinkList LA,LinkList LB) /*此算法将两个链表的首尾连接起来*/ Node *p, *q; p=LA; q=LB; while (p-next!=LA) p=p-next;/*找到表LA的表尾*/ while (q-next!=LB) q=q-next;/*找到表LB的表尾*/ q-next=LA; /*修改表LB 的尾指

23、针,使之指向表LA 的头结点*/ p-next=LB-next; /*修改表LA的尾指针,使之指向表LB 中的第一个结点*/ free(LB); return(LA); ,return,2.3.4 双向链表,双向链表:在单链表的每个结点里再增加一个指向其前趋的指针域prior。这样形成的链表中就有两条方向不同的链,我们称之为双 ( 向) 链表 (Double Linked List)。 双向链表的结构定义: typedef struct Dnode ElemType data; struct DNode *prior,*next; DNode,* DoubleList;,双链表的结构定义,双链

24、表的结点结构,前驱指针域,数据域,后继指针域,双向循环链表示意图,双向链表的前插操作,算法描述:欲在双向链表第i个结点之前插入一个的新的结点,则指针的变化情况如下:,a,b,c,s,p,双向链表的前插操作算法实现,void DlinkIns(DoubleList L,int i,ElemType e) DNode *s,*p; /*首先检查待插入的位置i是否合法*/ /* 若位置i合法,则让指针p指向它*/ s=(DNode*)malloc(sizeof(DNode); if (s) s-data=e; s-prior=p-prior; p-prior-next=s; s-next=p; p-

25、prior=s; return TRUE; else return FALSE; ,双向链表的删除操作,a,b,c,p,双向链表的删除操作算法实现,intDlinkDel(DoubleList L,int i,ElemType *e) DNode *p;/*首先检查待插入的位置i是否合法*/ /*若位置i合法,则让指针p指向它*/ *e=p-data; p-prior-next=p-next; p-next-prior=p-prior; free(p); return TRUE; ,双向链表应用举例,已知:设一个循环双链表L=(a,b,c,d)编写一个算法将链表转换为L=(b,a,c,d)。

26、算法思想:实际上是交换表中前两个元素的次序。 算法实现: void swap(DLinkList L) DNode * p,*q,*h; h=L-next; /* h指向表中的第一个结点,即a */ p=h-next; /* p指向b结点 */ q=h-prior; /* 保存a 结点的前驱 */ h-next=p-next; p-next-prior=h; /*变换指针指向*/ p-prior=q; p-next=h; L-next=p; ,return,*2.3.5 静态链表,基本概念: 游标实现链表的方法:定义一个较大的结构数组作为备用结点空间(即存储池)。当申请结点时,每个结点应含有两

27、个域:data域和next域。data域存放结点的数据信息,next域为游标指示器,指示后继结点在结构数组中的相对位置(即数组下标)。数组的第0个分量可以设计成表的头结点,头结点的next域指示了表中第一个结点的位置,为0表示静态单链表的结束。我们把这种用游标指示器实现的单链表叫做静态单链表(Static Linked List)。 静态链表的结构定义,静态链表的插入和删除操作示例。 基本操作: 初始化、分配结点与结点回收、前插操作、删除。,静态链表的结构定义,#define Maxsize= 链表可能达到的最大长度 typedef struct ElemType data; int curs

28、or; Component, StaticListMaxsize;,静态链表的插入和删除示意图,静态链表S中存储着线性表 (a,b,c,d,f,g,h,i),Maxsize=11,初始化,插入e,删除h,静态链表初始化,算法描述:初始化操作是指将这个静态单链表初始化为一个备用静态单链表。设space为静态单链表的名字,av为备用单链表的头指针,其算法如下: void initial(StaticList space,int *av) int k; space0-cursor=0;/*设置静态单链表的头指针指向位置0*/ for(k=0;kcursor=k+1; /*连链*/ spaceMaxs

29、ize-1=0; /*标记链尾*/ *av=1; /*设置备用链表头指针初值*/ ,静态链表分配结点与结点回收,分配结点 int getnode(int *av) /*从备用链表摘下一个结点空间,分配给待插入静态链表中的元素*/ int i=*av; *av=space*av.cur; return i; 结点回收 void freenode(int *av,int k) /*将下标为 k的空闲结点插入到备用链表*/ spacek.cursor=*av; *av=k; ,静态链表前插操作,算法描述: 1、先从备用单链表上取一个可用的结点; 2、将其插入到已用静态单链表第i个元素之前。 void

30、 insbefore(StaticList space,int i,int *av) j=*av ; /*j为从备用表中取到的可用结点空间的下标*/ av=spaceav-cursor;/*修改备用表的头指针*/ spacej-data=x; k=space0-cursor;/*k为已用静态单链表的第一个元素的下标值*/ for(m=1;mcursor; spacej-cursor=spacek-cursor; /*修改游标域*/ spacek-cursor=j; ,静态链表删除,算法描述:首先寻找第i -1个元素的位置,之后通过修改相应的游标域进行删除;在将被删除的结点空间链到可用静态单链表中,实现回收。 void delete(StaticList space;int i;int *av ) k=space0-cursor; for(m=1,mcursor ; j=spacek-cursor ; spacek-cursor=spacej-cursor;/*从删除第i个元素*/ spacej-cursor=*av;/*将第i 个元素占据的空间回收*/ av=j ; /*置备用表头指针以新值*/ ,return,2.

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论