线性链表

21
线线线线 线线线线 安安安安安安安安安 西 ctec.xjtu.edu.cn

Upload: sagittarius-lucas

Post on 31-Dec-2015

53 views

Category:

Documents


2 download

DESCRIPTION

线性链表. 西安交通大学计教中心 ctec.xjtu.edu.cn. 单链表的概念. 采用链式存储结构的线性表有单链表、双向链表、单循环链表以及双向循环链表等多种形式。 单链表用一组地址任意的存储单元存放线性表中的数据元素。由于逻辑上相邻的元素其物理位置不一定相邻,为了建立元素间的逻辑关系,需要在线性表的每个元素中附加其后继元素的地址信息。 这种地址信息称为指针。附加了其他元素指针的数据元素称为结点。. 结点定义. 结点包含数据域和指针域 , 结点结构可描述为 : - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 线性链表

线性链表线性链表

西安交通大学计教中心ctec.xjtu.edu.cn

Page 2: 线性链表

采用链式存储结构的线性表有单链表、双向链表、单循环链表以及双向循环链表等多种形式。

单链表用一组地址任意的存储单元存放单链表用一组地址任意的存储单元存放线性表中的数据元素。由于逻辑上相邻的元素线性表中的数据元素。由于逻辑上相邻的元素其物理位置不一定相邻,为了建立元素间的逻其物理位置不一定相邻,为了建立元素间的逻辑关系,需要在线性表的每个元素中附加其后辑关系,需要在线性表的每个元素中附加其后继元素的地址信息。继元素的地址信息。

这种地址信息称为指针。附加了其他元这种地址信息称为指针。附加了其他元素指针的数据元素称为结点。素指针的数据元素称为结点。

单链表的概念

Page 3: 线性链表

结点定义 结点包含数据域和指针域 , 结点结构可描述为 :

其中 data 域用来存放结点本身信息,类型由具体问题而定,本节也将其设定为 ElemType 类型,表示某一种具体的已知类型, next 域存放下一个元素地址。

Data next next

Page 4: 线性链表

带头结点单链表的逻辑结构 为了能顺次访问每个结点,需要保存单链表第一个结点的存储地址。这个地址称为线性表的头指针,本节用 head 表示。 为了操作上的方便,可以在单链表的头部增加一个特殊的头结点。头结点的类型与其他结点一样,只是头结点的数据域为空。 增加头结点避免了在删除或添加第一个位置的元素时进行的特殊程序处理。 head a1 a2 an

带头结点的单链表

Page 5: 线性链表

带头结点单链表的存储结构

head

38

数据域 数据域

38

22

94

86

a2 86

… … 94

… … a3 NULL

a1 22

存储地址

单链表存储结构示意图

Page 6: 线性链表

结点的 c++描述typedef struct LNode

{

ElemType data; // 数据域, ElemType代

// 表某种数据类型struct LNode *next; // 指针域

} *LinkList;这个定义是自引用类型的。换言之,每

个结点都包含另一个同类型结点的地址。

Page 7: 线性链表

单链表的类形式定义如下:class LinkList{

public:

LNode *head; // 定义头指针 LinkList() { // 构造函数

head=new LNode; // 建立头结点head->next=NULL; // 头结点的指针为空

}

int ListSize(); // 求单链表长度 // 返回表中指定序号结点的指针 LNode* GetElemPointer(int pos);

( 接下一页 … ...)

Page 8: 线性链表

( 接上页 )

// 向单链表第 i 个位置插入元素 x

void InsertList(int i, ElemType x);

// 从单链表中删除第 i 个结点 LNode* LinkList::DeleteList( int i);

// 在单链表中查找数据值为 x 的结点 LNode* Find( ElemType x ) ;};

这里将构造函数的实现直接放在了类定义中。如果要定义一个不带头结点的单链表,则只需定义空的头指针,即 head=NULL 。

Page 9: 线性链表

指针操作

假如 p 为指向某一结点的指针,则该结点的数据域用 p->data 表示,该结点的指针域用 p->next 表示。它们都是变量,可以被赋值,也可向其他变量赋值。例如 : 假定 data 为整型变量 , 则

p->data=5; p->next=NULL;

将结点变为 :

5 NULL next

Page 10: 线性链表

aiaiai-1ai-1 ai-1ai-1

aiaiai-1ai-1 ai+1ai+1

q

q p

p

如果 p 为指向结点 ai 的指针,那么 p->next 就是指向 ai 后继 ai+1 的指针;若 q 为另一指针变量

• q=p 指针 p 指向指针 q 所指的结点• q=p->next 指针 p 指向指针 q 所指结点的后继

Page 11: 线性链表

p=p->next 指针 p 向后移动一个结点

p

ai-1 ai ai+1

p

Page 12: 线性链表

常用算法( 1 )求单链表的长度

单链表长度不定,要确定链表长度需要走遍表中所有结点才能算出。

int LinkList::ListSize() { LNode *p=head->next; //p 指向第一个元素所在结点 int len=0; while( p!=NULL ) { // 逐个检测 p 结点存在与否 len++;

p=p->next; // 指针后移 } return len;

} 为了保持头指针不变,使用了指针 p 在链表中移动。

Page 13: 线性链表

( 2 )返回单链表中指定序号的结点的指针LNode* LinkList::GetElemPointer(int pos){

if(pos<0) { return NULL; } // 该位置不存在 else if(pos==0) { return head; } else {

LNode *p=head->next; //p 为首元结点指针int k=1;while( p!=head && k<pos ) {

p=p->next; k++; } if(k==pos&&p!=head) return p; // 返回第 pos 个结点

指针else return NULL; // 该位置不存在

}}

Page 14: 线性链表

( 3 )从单链表中删除第 i 个结点为了从单链表中删除第 i 个结点,需进行如下操作: ① 若第 i 个结点存在则找到第 i 和第 i-1 个结点的指针 p 和 q

② 通过语句 q->next=p->next 将第 i-1 个结点的指针域赋值为第 i+1 个结点的指针,将第 i 个结点从链表中断开。

③ 释放第 i 个结点所占空间以便于重用。

q p

ai-1 ai ai+1

q->next=p->next;q->next=p->next;

delete p;delete p;

Page 15: 线性链表

LNode* LinkList::DeleteList( int i ) { if(i<1) cout<<” 不存在第” <<i<<” 个元素” ; else {

LNode *p=head; //p 指向头结点 ( 第 0 个结点 ) LNode *q; //q 和 p 最终分别指向第 i-1 和第 i 个结点 int k=0; while( p!=NULL&&k<i ) {

q=p;p=p->next;k++;

} if(p==NULL) cout<< i<<” 超出链表长度” ; else {

q->next=p->next; // 从链表中删除该结点delete p; // 释放结点 p

}}

}

Page 16: 线性链表

( 4 )在第 i 个位置插入新结点 x在链表的第 i 个位置插入一个新结点,需进行如下操作:① 首先找到第 i-1 个结点的指针 p

② 建立新结点 s 并通过语句 s->next=p->next 将其指针指向第 i 个结点

③ 通过语句 p->next=s 将第 i-1 个结点的指针指向新结点

s->data = x;s->data = x;

s->next=p->next;s->next=p->next;

p->next=s;p->next=s;

ai-1ai-1 aiai

p

xx

s

12

Page 17: 线性链表

void LinkList::InsertList( int i, ElemType x)

{

LNode *p=head;

p=GetElemPointer(i-1); //p 最终将指向第 i-1 个结点 if(!p)

cout<< i<<" 位置无法插入元素 ";

else {

LNode *s=new LNode; // 建立新结点 s

s->data = x;

s->next=p->next; // 定义结点 s 的指针域 p->next=s; //修改结点 p 的指针域

}

}

Page 18: 线性链表

( 4 )在单链表中查找数据值为 x 的结点可以按照数据元素本身的值进行查找,也可以按照数

据元素的某个属性进行查找。这里仅给出按照数据元素本身的值进行查找的算法。

LNode* LinkList::Find( ElemType x )

{

LNode *p=head->next; //p 指向第一个元素所在结点

while ( p!=NULL && p->data!=x )

p = p->next;

return p;

}

Page 19: 线性链表

其他形式的链表 ( 1 )单循环链表

通过把单链表最后一个结点的指针改为指向第一个结点,就可以把一个单链表改造成单循环链表。因为单链表最后一个结点的指针总是空值,所以这样的修改总是可行的。

headhead

...a1 a2 an

headhead

Page 20: 线性链表

2)双向链表如果每个链表结点既有指向下一个元素的指针,又

有指向前一个元素的指针,那么这种链表就是双向链表,双向链表结点的定义只需在单链表结点定义的基础上增加一个前向指针即可。

headhead

...

... ana2a1

Page 21: 线性链表

链表应用举例 约瑟夫环问题可以解释为:将整数 1至 n围成一个圆圈,假定从某个整数开始顺时针从 1 数到m时,令该位置整数出列;然后再从下一数开始,顺时针从 1 数到第m时再令其出列,如此下去,直到圆圈中无整数为止。请写出所有数字出列的顺序。

【例 2-2 】