1 / 37

第 3 章 链式存储结构

第 3 章 链式存储结构. 第 3 章 链式存储结构. 3.1 线性表的链式存储结构 3.1.1 单链表上的基本运算 3.1.2 循环链表 3.1.3 双向链表 3.2 线性表的顺序和链式存储结构的比较 3.3 应用举例及分析 习题. 3.1 线性表的链式存储结构. 线性表的链式存储结构特点: 用一组 地址任意 的存储单元存放线性表中的数据元素。. 结点: 数据域 ( 数据元素的信息 ) + 指针域 ( 指示直接后继存储位置 ) = 结点 ( 表示数据元素或数据元素的映象 ). 链表: 以“结点的序列”表示线性表称作 链表。.

Download Presentation

第 3 章 链式存储结构

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. 第3章 链式存储结构

  2. 第3章 链式存储结构 3.1线性表的链式存储结构 3.1.1 单链表上的基本运算 3.1.2 循环链表 3.1.3 双向链表 3.2线性表的顺序和链式存储结构的比较 3.3应用举例及分析 习题

  3. 3.1线性表的链式存储结构 线性表的链式存储结构特点:用一组地址任意的存储单元存放线性表中的数据元素。 结点: 数据域(数据元素的信息)+指针域(指示直接后继存储位置)=结点(表示数据元素或数据元素的映象) 链表:以“结点的序列”表示线性表称作链表。

  4. 3.1线性表的链式存储结构 (a) 线性链表的图示; (b) 线性链表的一般表示 线性链表示意图

  5. a1 a2 … ... an ^ 线性表为空表时, 头结点的指针域为空 头指针 头指针 头指针 头结点  以线性表中第一个数据元素a1的存储地址作为线性表的地址,称作线性表的头指针。 有时为了操作方便,在第一个结点之前虚加一个“头结点”,以指向头结点的指针为链表的头指针。

  6. 单链表的数据结构: type struct node {char data; struct node *next }LINKLIST; 结点的申请、释放: 若LINKLIST head,*new;则可用: head=malloc (sizeof (LINKLIST));申请结点 free (head);释放结点

  7. 线性表; (b) 单链表 • 线性表和链表的存储结构

  8. 3.1.1单链表上的基本运算 一、建立单链表 假设线性表中结点的数据类型是字符,逐个输入这些字符型的结点,并以 ‘$’为输入结束标记。动态地建立单链表的常用方法有如下两种: 1、头插法建表 该方法从一个空表开始,重复读入数据,生成新结点,将读入数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直到读入结束标志为止。算法如下:

  9. main( ) { LINKLIST * head , * new ; Char ch ; while ( ( ch=getchar ()) !=’$’) new=malloc ( sizeof ( LINKLIST)) ; new->data=ch ; new->next= head ; head =new ; }

  10. 2、尾插法建表 头插法建立链表虽然算法简单,但生成的链表中结点的次序和输入的顺序相反。若希望二者次序一致,可采用尾插法建表。该方法是将新结点插入到当前链表的表尾上,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点。 算法如下:

  11. main( ) {LINKLIST * head , * last , * new ; Char ch ; new=malloc ( sizeof (LINKLIST )) ; head=new ; last=new ; new->next=NULL ; while ( ch=getchar (1) !=’$’) {new=malloc ( sizeof ( LINKLIST)) ; new->data=ch ; last->next=new ; last=new ; new->next=NULL ;}

  12. 二、查找运算 1、按序号查找第i个结点 在有头指针的单链表中找第i个结点,算法 为:设head为头,p为当前结点,0为头结点序号,i为计数器,则可使p依次下 移找结点,并使i同时递增记录结点号,找到返回结点地址,找不到返回NULL。

  13. 算法如下: LINKLIST *get ( int i , LINKLIST *head ) {int j=0 ; LINKLIST *p=head ; while (cj<i) &&(p->next !=NULL ) {p=p->next ; i++ ; } if (i= =j ) return p ; else return NULL ; }

  14. 2、按值查找 按值查找是在链表中,查找是否有结点值等于给定值x的结点,若有,则返回首次找到的其值为x的结点的存储位置;否则返回NULL。查找过程从开始结点出发,顺着链表逐个将结点的值和给定值x作比较。 其查找流程及算法如下:

  15. 线性链表查找运算流程图

  16. 按值查找算法: LINKLIST * locate (Char x , LINKLIST * head ) {LINKLIST *p ;; p=head->next ; while ( p->data= = x ) return p ; else p=p->next ; } return NULL ; }

  17. 三、插入运算 插入运算是将值为x的新结点插入到表的第i个结点的位置上,即插入到ai-1与ai之间。因此,首先找到ai-1的存储位置p,然后生成一个数据域为x的新结点*p,并令结点*p的指针域指向新结点,新结点的指针域指向结点ai。从而实现三个结点ai-1,x和ai之间的逻辑关系的变化。 new=malloc ( ); new.data=x ; new.next=p->next ; p.next=new;

  18. x 在已知结点之后插入结点示意图

  19. 四、删除运算 删除运算是将表的第i个结点删去。因为在单链表中结点ai的存储地址是在其直接前趋结点ai-1的指针域next中,所以首先找到ai-1的存储位置p,然后令p–>next指向ai的直接后继结点,即把ai从链上摘下。最后释放结点ai的空间,将其归还给“存储池”。

  20. 1、删除已知结点的后继结点 删除*p结点的后继结点示意图 p

  21. 2、在单链表上删除第i个元素 在单链表上删除第i个元素的算法: int delete node ( int ; LINKLIST * head ) { LINKLIST * p , *q ; int j , r ; r=1 j=0 p=head ; while ( (j<i) && (p->next !=NULL ) ) {p=p->next ; j++ ; } if ( p->next =NULL || j>i-1 ) /*i<1 , 比表的头位置还小*/ r=0 ; else { p=p->next ; p->next=q->next ; free (q) ; } return r ;

  22. an head head a1 3.1.2.循环链表 循环链表是一种首尾相接的链表。将单链表的终点的指针改为指向单链表的第一个结点,就成为单链循环链表。 带头结点的非空循环链表 带头结点的空循环链表

  23. an a1 rear 特点:从表中任一结点出发均可找到表中其他所有结点。 因实际需要,经常采用尾指针表示循环链表,这样a1=rear->next->next,an=rear,显然,查找a1和an都很方便。 rear->next->next 用尾指针表示的非空循环单链表

  24. 3.1.3 双向链表 概念:在链表的结点中设定两个结点指针域,使链表中有两条不同方向的链,称为双向链表。 数据结构: typedef struct dnode {datatype data ; struct dnode * prior ,* next; } DLINKLIST ; 双向链表中结点的结构:

  25. 与单链表类似,双向链表也由头指针唯一确定,将头结点与尾结点链起来构成循环链表称双向循环链表。与单链表类似,双向链表也由头指针唯一确定,将头结点与尾结点链起来构成循环链表称双向循环链表。 双向链表特点:双向链表是一种对称结构,克服了单链表上指针单向性的缺点,既有前向链,又有后向链,这就使得数据元素的插入、删除操作都很方便。 建双向链表后,有 p= ( p->prior ) ->next= ( p->next ) ->prior

  26. x 1、双向链表中插入结点 与单链表的插入和删除操作不同的是,在双链表中插入和删除必须同时修改两个方向上的指针。 ① ③ ④ ② 插入结点示意图

  27. 算法如下: t=malloc ( ) ; t->data=x ; t->prior = p->prior ; t->next = p ; ( p->prior ) ->next= t; p->prior = t;

  28. P 2、双链表中删除p指针指向的结点 ① ② 删除双链表中已知结点*P

  29. 双链表中删除p指针指向的结点的算法: p->prior->next= p->next ; p->next->prior = p->prior ; free ( p );

  30. 3.2线性表的顺序和链式存储结构的比较 1、空间性能的比较 顺序表的存储空间是静态分布的,链表的存储空间是动态分布的。与其它链式存储一样,没有溢出现象。但要牺牲一部分附加空间。 2、时间性能比较 顺序表是随机存储结构,其时间复杂度为O(1),若链表中的操作主要查找、读取时,采用顺序表结构为宜,而操作为删除、插入元素占多时,采用链表比较合适。它不存在搬家问题。

  31. 3.3应用举例及分析 例3.1 写出一个计算链表中结点个数的算法,并依次打印出链表中元素的值。 算法如下: Int count (LINKLIST *head) {int i=0; LINKLIST *p; p=head->next; while(p!=NULL) {i++; printf(“%c”,p->data); p=p->next;} return i; }

  32. 例3.2 写一算法,将值为x的结点插在链表中第一个值为a的结点之后,如果值为a的结点不存在,则插入在表尾。 其算法如下: Void ifinsert (DATATYPE2 x, DATATYPE2 a,LINKLIST *head) {LINKLIST *p_new,*p; int flag=1; p_new=malloc(sizeof(LINKLIST)); p_new->data=x; p=head; while(p->data!=NULL && flag) {if (p->data==a) {p_new->next=p->next; p->next=p_new; flag=0;} else p=p->next;} if(flag) {p->next=p_new; p_new->next=NULL;} }

  33. 例3.3在一个非递减有序顺序表中,插入一个值为x的元素,使插入后的顺序表仍为非递减有序表,用带头结点的单链表结构编写算法。其算法为:例3.3在一个非递减有序顺序表中,插入一个值为x的元素,使插入后的顺序表仍为非递减有序表,用带头结点的单链表结构编写算法。其算法为: insert_order(DATATYPE2 x, LINKLIST * head) { LINKLIST * pr, * pn; pr = head; pn = head->next; While(pn ! = NULL && pn-> data < x) {pr = pn; pn = pn->next;} insertafter(x,pr); }

  34. 例3.4将一个用单链表存储的线性表T=(a1,a2,…,am-1,am)置换成(am,am-1,…, a2, a1,),实现的算法中辅助变量只能用指针(单链表不带头结点)。其算法如下: LINKLIST * inverlink(LINKLIST * head) { LINKLIST * p, * q, * r; q = NULL;p = head; While(p ! = NULL) {r = q; q = p; p = p->next; q->next = r;} return q; }

  35. L a b c d 例3.5 写出下列双向链表中逻辑交换结点a和b的算法,不设辅助结点,只设辅助指针。 p = L->next; L->next = p->next; (L->prior)->next = p; p->prior = L->prior; (p->next)->prior = L; L->prior = p; p->next = L; L = p;

  36. a b c d L 例3.6 写出下列双向链表中逻辑交换结点a和b的算法,不设辅助结点,只设辅助指针 p = L->next;L->next = p->next; (L->prior)->next = p;p->prior = L->prior; (p->next)->prior = L; L->prior = p;p->next = L; L = p;

  37. 习题 3.1 分析单链表、循环链表和双向链表的相同点和不同点,及各自特点。 3.2 设有两个线性表A和B皆是单链表存储结构,同一个表中的元素各不相同,且递增有序。写一算法,构成一个新的线性表C,使C为A和B的交集,且C中元素也递增有序。 3.3 设有两个按元素值递增有序的单链表A和B,编一程序将A表和B表归并成一个新的递增有序的单链表C(值相同的元素均保留在C表中)。 3.4 设L为带头节点的单链表,按下面两种情况分别编写算法删除表中值相同的多余元素。 (1)顺序表,元素值递增有序; (2)顺序表,元素值无序。

More Related