双向链表数据结构链表
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
储单元,存储线性表的各个元素,为了表示每个元素与其后 继元素之间的逻辑关系,每个元素除了需要存储自身信息外,
还要存储一个指示其后继元素的信息,即后继元素的地址。
这样每个结点包括两个域:值域(data)----存放元素本身
的信息;指针域(link)----存放其后继结点的存储位置。
值域 指针域
例
A
100
数据结构
链表
r=C;
free(B); /*释放B表的头结点*/ while (p&&q) { if (p->data<q->data) { s=p; p=p->next; } else { s=q; q =q->next; } /*从原AB表上摘下较小者*/ s->next=r->next; r->next=s; r=r->next; } /*while*/ if (p) r->next=p; /*将A表剩余部分插入到C表的尾部*/ /*插入到C表的尾部*/
算法 2 单链表的插入 int insert_link( LinkList list, PNode p, DataType x ) 算法 3 单链表结点的删除 int delete_link(LinkList list, DataType x )
数据结构 算法1 创建空单链表
链表
分析:申请一表头结点的存储空间,并将该结点指针域置空 list
k0
插入x至palist[p]
k1 p k2 x k3
k4
k5 k6
表已满!
数据结构
链表
顺序表的删除过程 int delete_seq( PSeqList palist, int p) k0 k0 k1 k2 p k3 k4
k1
p
k2 x3 k
k4 3 k5 4
删除位置p的元素
p
k5
Байду номын сангаасk6
表长减1
k6 5 k6
^
LinkList CreateNullist_link(void) { LinkList list=(LinkList)malloc(sizeof(struct Node)); //申请表头结点存储空间 if( list != NULL) list->next=NULL; else printf(“Out of space!\n”); //创建失败 return(list); }
注:图中用“^”表示空指针,在算 法中用NULL表示。
h
A 图2
B
C
D
E
F ^
单链表h的逻辑结构示意图
数据结构
链表
2、单链表的类型定义
typedef struct Node *PNode; /* 结点指针类型 */ struct Node /* 单链表结点结构 */ { DataType data; /* 值域 */ struct Node *next; /* 指针域 */ }; 为提高可读性,可定义单链表类型如下: typedef struct Node *LinkList; LinkList list; /* 定义一单链表list */ PNode p; /* 定义一单链表结点指针*/ 则指针p所指结点的两个域表示为: 值 域: p->data 指针域: p->next
× ×
ki+1
q
x
q llink =p;
p rlink llink=q;
q rlink =p rlink;
p rlink=q;
图2.12
双链表的结点插入
数据结构
链表
cdlist
k0
k1
… …
kn-1
图2.13 带头结点的双链表
数据结构
链表
对于循环双链表,可作如下封装:
struct CDoubleList
数据结构
链表
Josephus问题
n8 n7 n6
n1
S=1; m=4;
n2 n3 n4 n5
n4 n8 n5 n2 n1 n3 n7 n6
数据结构 现以n=12,s=1,m=4为例, 初始队列如右图:
链表
数据结构
链表
若以顺序表求解,从n1开始数第4个后,n4出列,然后 将其后的所有元素前移。
数据结构
B
105
C
110
数据结构 线性表h=(A,B,C,D,E,F)的 链式存储结构如图1所示。 h 由于这种链表中,每个结
链表
点只有一个指针域,故又称为
单链表。指向链表中第一个结 点的指针,称为这个链表的头
指针(h)。
图1
线性表h的链式 存储结构
数据结构
链表
h
在讨论链表时,主要关心的 只是线性表中元素之间的逻辑顺 序,而不是每个元素在存储器中 的位置,所以通常可把图1的单 链表,更加直观地表示成图2所 示的用箭头相链接的结点序列, 其中h代表头指针。
注:算法中malloc为内存申请函数,需头文件stdlib.h支持。
数据结构
链表
算法2 单链表的插入(在list 带有头结点的单链表中,在p 所指结点后面插入元素x)
list
A
p
B
②
C
q X ^
①
D
^
②p->next=q;
① q->next=p->next;
list
A
p
B
q
X
C
D
^
图6 插入结点示意图
数据结构
链表
以上操作暴露出顺序表的问题:
1、在做插入或删除元素的操作时,会产生大量的数据元素
移动;
2、对于长度变化较大的线性表,要一次性地分配足够的存
储空间,但这些空间常常又得不到充分的利用;
3、线性表的容量难以扩充。 一种较好的解决方式:链式存储结构
数据结构
链表
单链表
1、结点结构
线性表的链接表示的目的是用一组可以是不连续的存
值域
指针域
p
data
next
数据结构
链表
3、头结点的概念与作用
为使运算简单,可以在单链表的第一个结点之前另加一
个结点,称之为头结点。头结点的data字段可以存放与整 个链表相关的信息(例如元素个数等),也可以不存任何
信息,头结点的link字段指向第一个结点,如图3所示。
list
头结点
A
B
….
F
图3 带头结点的单链表
数据结构
链表
pllink ki-1
p
prlink ki ki+1
rlink
llink
p llink rlink= prlink p rlink llink= pllink 图2.12 双链表的结点删除
数据结构
链表
双链表的插入(在p后面插入一个结点)
p ki-1 ki prlink
数据结构
链表
双向链表类型声明
struct DoubleNode ; typeef struct DoubleNode *PDoubleNode; struct DoubleNode /* 双链表结点结构 */ { DataType data; pDoubleNode llink, rlink; }; struct DoubleList /* 双链表类型 */ { PDoubleNode head; /* 指向第一个结点 */ PDoubleNode rear; /* 指向最后一个结点 */ }; typedef struct DoubleList * PDoubleList; PDoubleList pdlist; /* pdlist是指向双链表类型 的指针变量 */
{
PDoubleNode head; } CDoubleList cdlist ;
循环双链表有如下性质: p==p->rlink-llink==p->llink-rlink
数据结构 单链表应用举例
链表
【例2.4】 有一线性表的单链表表示 (a1,a2,… ,an) ,设计一算法将该单链表 逆置成逆线性表(an,an-1,… ,a1)。 算法思路:首先将单链表拆开成一个空表H和一个不带头结点的单链表,然后 将不带头结点的单链表从第一个结点开始依次取出每个结点,将其插入到H单 链表的第一个位置。算法如下: void reverse_LinkList (Linklist H) { LinkList p; p=H->next; /*p指向第一个数据结点*/ H->next=NULL; /*将原链表置为空表H*/ while (p) { q=p; p=p->next; q->next=H->next; /*将当前结点插到头结点的后面*/ H->next=q;
②删除结点q
② free(q);
list
A
p
B
D
E
^
图7 删除结点示意图
数据结构
链表
删除结点算法:
首先在list 带有头结点的单链表中找到第一个值为x的 结点q,并记录其前驱结点的位置p,然后通过指针修正删除 结点q。
删除实现: q=p->next;
p->next=q->next;
free(q);
int josephus_ SeqList (PSeqList josephus_seq, int s, int m)
数据结构
链表
线性表的链接实现(链表)
上讲回顾 单链表 1、结点结构 2、单链表的类型定义 3、头结点的概念与作用
4、单链表的常用算法
5、应用实例
6、小结
7、思考题
数据结构
链表
上讲回顾
上讲主题:线性表的顺序实现(顺序表)
地址 元 素
1000 1001 1002 1003 1004 1005
k0 k1 k2 k3 k4 k5 k6
在顺序表中,逻辑关系上相 邻的两个结点,在物理上也是相 邻的,故可以按下标随机存取任 一元素,
1006
数据结构 顺序表的插入过程 int insert_seq( PSeqList palist, int p, DataType x ) palist[8]
链表
p
k0 k1 k2 k3
k4 3 k5 4 k6 5 k6
数据结构 clist k0 k1
链表
….
kn-1
clist
k0
k1
….
kn-1
图2.10 循环链表结构
数据结构
链表
clist k0 k1
….
kn-1
带头结点循环链表结构
数据结构
链表
双向链表
优点:既可以方便的找前驱,也可以找后继。
head rear
^
k0
k1
k2
… …
kn-1 ^
图2.15
双向链表
LinkList merge_LinkList (LinkList A, LinkList B) { /*设A、B均为带头结点的单链表*/ LinkList C; LinkList p,q,r; p=A->next; q=B->next; C=A; /*借助A表的头结点构造空C表*/ C->next=NULL;
注:设置表头结点的目的是统一空链表与非空链表的操作,简化链表操 作的实现。带头结点的空链表表示为:list->next==NULL;而不带头结点 的空链表只能表示为list==NULL;
数据结构
链表
4、单链表的常用算法(带头结点)
算法 1 创建空单链表
LinkList createNullist_link( void )
动态插入和删除
数据结构
链表
2.4.3 循环链表
将单链表的形式稍作改变,不让最后一个结 点的指针为NULL,而让它指向头一个结点,这样 就得到了循环链表。 它的优点是:从循环链表中任一结点出发, 都能访问遍所有结点。 实际应用中,常让指向循环链表的指针指向 最后一个结点 ,这样无论是找第一个结点还是找 最后一个结点都很方便。
else
r->next=q; /*将B表剩余部分插入到C表的尾部*/
} /*end*/
数据结构
链表
应用举例—Josephus问题*
问题描述:
设有 n 个人围坐在一个圆桌周围,现从第 s 个人 开始报数,数到第 m 的人出列,然后从出列的下一 个人重新开始报数,数到第m的人又出列,…,如此 反复直到所有的人全部出列为止。Josephus问题是: 对于任意给定的n,s和m,求出按出列次序得到的n个 人员的序列。 现以n=8,s=1,m=4为例,问题的求解过程如图 2-15所示。图中s1指向开始报数位置, 若初始的顺 序为 n1,n2,n3,n4,n5,n6,n7,n8。则问题的解 为n4,n8,n5,n2,n1,n3,n7,n6。
} /*while*/
}
数据结构
链表
【例2.5】假设有两个元素值递增有序的线性表A和B,均以
带头结点的单链表作为存储结构,编写算法将A和B归并成 一个按元素值递增有序排列的线性表C,并要求利用原线
性表A和B的结点空间存放线性表C。
算法思路:利用A、B两表有序的特点,依次扫描A和B的元 素,比较当前的元素的值,将当前值较小者摘下,插入到 C表的尾部,如此直到一个单链表扫描完毕,然后将未完 的那个单链表中余下部分连到C即可。
数据结构
链表
插入结点算法:
首先生成待插入结点q,将其值域置为x,然后
通过修正指针将结点q插入到结点p之后。 插入实现: q->data=x; p->next=q; //生成待插入结点
q->next=p->next;
数据结构 算法3 单链表结点的删除
① list
链表
A
p
B
q
X
D
E
^
① p->next=q->next;
还要存储一个指示其后继元素的信息,即后继元素的地址。
这样每个结点包括两个域:值域(data)----存放元素本身
的信息;指针域(link)----存放其后继结点的存储位置。
值域 指针域
例
A
100
数据结构
链表
r=C;
free(B); /*释放B表的头结点*/ while (p&&q) { if (p->data<q->data) { s=p; p=p->next; } else { s=q; q =q->next; } /*从原AB表上摘下较小者*/ s->next=r->next; r->next=s; r=r->next; } /*while*/ if (p) r->next=p; /*将A表剩余部分插入到C表的尾部*/ /*插入到C表的尾部*/
算法 2 单链表的插入 int insert_link( LinkList list, PNode p, DataType x ) 算法 3 单链表结点的删除 int delete_link(LinkList list, DataType x )
数据结构 算法1 创建空单链表
链表
分析:申请一表头结点的存储空间,并将该结点指针域置空 list
k0
插入x至palist[p]
k1 p k2 x k3
k4
k5 k6
表已满!
数据结构
链表
顺序表的删除过程 int delete_seq( PSeqList palist, int p) k0 k0 k1 k2 p k3 k4
k1
p
k2 x3 k
k4 3 k5 4
删除位置p的元素
p
k5
Байду номын сангаасk6
表长减1
k6 5 k6
^
LinkList CreateNullist_link(void) { LinkList list=(LinkList)malloc(sizeof(struct Node)); //申请表头结点存储空间 if( list != NULL) list->next=NULL; else printf(“Out of space!\n”); //创建失败 return(list); }
注:图中用“^”表示空指针,在算 法中用NULL表示。
h
A 图2
B
C
D
E
F ^
单链表h的逻辑结构示意图
数据结构
链表
2、单链表的类型定义
typedef struct Node *PNode; /* 结点指针类型 */ struct Node /* 单链表结点结构 */ { DataType data; /* 值域 */ struct Node *next; /* 指针域 */ }; 为提高可读性,可定义单链表类型如下: typedef struct Node *LinkList; LinkList list; /* 定义一单链表list */ PNode p; /* 定义一单链表结点指针*/ 则指针p所指结点的两个域表示为: 值 域: p->data 指针域: p->next
× ×
ki+1
q
x
q llink =p;
p rlink llink=q;
q rlink =p rlink;
p rlink=q;
图2.12
双链表的结点插入
数据结构
链表
cdlist
k0
k1
… …
kn-1
图2.13 带头结点的双链表
数据结构
链表
对于循环双链表,可作如下封装:
struct CDoubleList
数据结构
链表
Josephus问题
n8 n7 n6
n1
S=1; m=4;
n2 n3 n4 n5
n4 n8 n5 n2 n1 n3 n7 n6
数据结构 现以n=12,s=1,m=4为例, 初始队列如右图:
链表
数据结构
链表
若以顺序表求解,从n1开始数第4个后,n4出列,然后 将其后的所有元素前移。
数据结构
B
105
C
110
数据结构 线性表h=(A,B,C,D,E,F)的 链式存储结构如图1所示。 h 由于这种链表中,每个结
链表
点只有一个指针域,故又称为
单链表。指向链表中第一个结 点的指针,称为这个链表的头
指针(h)。
图1
线性表h的链式 存储结构
数据结构
链表
h
在讨论链表时,主要关心的 只是线性表中元素之间的逻辑顺 序,而不是每个元素在存储器中 的位置,所以通常可把图1的单 链表,更加直观地表示成图2所 示的用箭头相链接的结点序列, 其中h代表头指针。
注:算法中malloc为内存申请函数,需头文件stdlib.h支持。
数据结构
链表
算法2 单链表的插入(在list 带有头结点的单链表中,在p 所指结点后面插入元素x)
list
A
p
B
②
C
q X ^
①
D
^
②p->next=q;
① q->next=p->next;
list
A
p
B
q
X
C
D
^
图6 插入结点示意图
数据结构
链表
以上操作暴露出顺序表的问题:
1、在做插入或删除元素的操作时,会产生大量的数据元素
移动;
2、对于长度变化较大的线性表,要一次性地分配足够的存
储空间,但这些空间常常又得不到充分的利用;
3、线性表的容量难以扩充。 一种较好的解决方式:链式存储结构
数据结构
链表
单链表
1、结点结构
线性表的链接表示的目的是用一组可以是不连续的存
值域
指针域
p
data
next
数据结构
链表
3、头结点的概念与作用
为使运算简单,可以在单链表的第一个结点之前另加一
个结点,称之为头结点。头结点的data字段可以存放与整 个链表相关的信息(例如元素个数等),也可以不存任何
信息,头结点的link字段指向第一个结点,如图3所示。
list
头结点
A
B
….
F
图3 带头结点的单链表
数据结构
链表
pllink ki-1
p
prlink ki ki+1
rlink
llink
p llink rlink= prlink p rlink llink= pllink 图2.12 双链表的结点删除
数据结构
链表
双链表的插入(在p后面插入一个结点)
p ki-1 ki prlink
数据结构
链表
双向链表类型声明
struct DoubleNode ; typeef struct DoubleNode *PDoubleNode; struct DoubleNode /* 双链表结点结构 */ { DataType data; pDoubleNode llink, rlink; }; struct DoubleList /* 双链表类型 */ { PDoubleNode head; /* 指向第一个结点 */ PDoubleNode rear; /* 指向最后一个结点 */ }; typedef struct DoubleList * PDoubleList; PDoubleList pdlist; /* pdlist是指向双链表类型 的指针变量 */
{
PDoubleNode head; } CDoubleList cdlist ;
循环双链表有如下性质: p==p->rlink-llink==p->llink-rlink
数据结构 单链表应用举例
链表
【例2.4】 有一线性表的单链表表示 (a1,a2,… ,an) ,设计一算法将该单链表 逆置成逆线性表(an,an-1,… ,a1)。 算法思路:首先将单链表拆开成一个空表H和一个不带头结点的单链表,然后 将不带头结点的单链表从第一个结点开始依次取出每个结点,将其插入到H单 链表的第一个位置。算法如下: void reverse_LinkList (Linklist H) { LinkList p; p=H->next; /*p指向第一个数据结点*/ H->next=NULL; /*将原链表置为空表H*/ while (p) { q=p; p=p->next; q->next=H->next; /*将当前结点插到头结点的后面*/ H->next=q;
②删除结点q
② free(q);
list
A
p
B
D
E
^
图7 删除结点示意图
数据结构
链表
删除结点算法:
首先在list 带有头结点的单链表中找到第一个值为x的 结点q,并记录其前驱结点的位置p,然后通过指针修正删除 结点q。
删除实现: q=p->next;
p->next=q->next;
free(q);
int josephus_ SeqList (PSeqList josephus_seq, int s, int m)
数据结构
链表
线性表的链接实现(链表)
上讲回顾 单链表 1、结点结构 2、单链表的类型定义 3、头结点的概念与作用
4、单链表的常用算法
5、应用实例
6、小结
7、思考题
数据结构
链表
上讲回顾
上讲主题:线性表的顺序实现(顺序表)
地址 元 素
1000 1001 1002 1003 1004 1005
k0 k1 k2 k3 k4 k5 k6
在顺序表中,逻辑关系上相 邻的两个结点,在物理上也是相 邻的,故可以按下标随机存取任 一元素,
1006
数据结构 顺序表的插入过程 int insert_seq( PSeqList palist, int p, DataType x ) palist[8]
链表
p
k0 k1 k2 k3
k4 3 k5 4 k6 5 k6
数据结构 clist k0 k1
链表
….
kn-1
clist
k0
k1
….
kn-1
图2.10 循环链表结构
数据结构
链表
clist k0 k1
….
kn-1
带头结点循环链表结构
数据结构
链表
双向链表
优点:既可以方便的找前驱,也可以找后继。
head rear
^
k0
k1
k2
… …
kn-1 ^
图2.15
双向链表
LinkList merge_LinkList (LinkList A, LinkList B) { /*设A、B均为带头结点的单链表*/ LinkList C; LinkList p,q,r; p=A->next; q=B->next; C=A; /*借助A表的头结点构造空C表*/ C->next=NULL;
注:设置表头结点的目的是统一空链表与非空链表的操作,简化链表操 作的实现。带头结点的空链表表示为:list->next==NULL;而不带头结点 的空链表只能表示为list==NULL;
数据结构
链表
4、单链表的常用算法(带头结点)
算法 1 创建空单链表
LinkList createNullist_link( void )
动态插入和删除
数据结构
链表
2.4.3 循环链表
将单链表的形式稍作改变,不让最后一个结 点的指针为NULL,而让它指向头一个结点,这样 就得到了循环链表。 它的优点是:从循环链表中任一结点出发, 都能访问遍所有结点。 实际应用中,常让指向循环链表的指针指向 最后一个结点 ,这样无论是找第一个结点还是找 最后一个结点都很方便。
else
r->next=q; /*将B表剩余部分插入到C表的尾部*/
} /*end*/
数据结构
链表
应用举例—Josephus问题*
问题描述:
设有 n 个人围坐在一个圆桌周围,现从第 s 个人 开始报数,数到第 m 的人出列,然后从出列的下一 个人重新开始报数,数到第m的人又出列,…,如此 反复直到所有的人全部出列为止。Josephus问题是: 对于任意给定的n,s和m,求出按出列次序得到的n个 人员的序列。 现以n=8,s=1,m=4为例,问题的求解过程如图 2-15所示。图中s1指向开始报数位置, 若初始的顺 序为 n1,n2,n3,n4,n5,n6,n7,n8。则问题的解 为n4,n8,n5,n2,n1,n3,n7,n6。
} /*while*/
}
数据结构
链表
【例2.5】假设有两个元素值递增有序的线性表A和B,均以
带头结点的单链表作为存储结构,编写算法将A和B归并成 一个按元素值递增有序排列的线性表C,并要求利用原线
性表A和B的结点空间存放线性表C。
算法思路:利用A、B两表有序的特点,依次扫描A和B的元 素,比较当前的元素的值,将当前值较小者摘下,插入到 C表的尾部,如此直到一个单链表扫描完毕,然后将未完 的那个单链表中余下部分连到C即可。
数据结构
链表
插入结点算法:
首先生成待插入结点q,将其值域置为x,然后
通过修正指针将结点q插入到结点p之后。 插入实现: q->data=x; p->next=q; //生成待插入结点
q->next=p->next;
数据结构 算法3 单链表结点的删除
① list
链表
A
p
B
q
X
D
E
^
① p->next=q->next;