线性表——双链表和循环双链表
双链表的定义在链表那里就介绍过了,这里就不重复介绍了。
双链表和循环双链表
同样地,我们用结构体来声明链表中的一个结点:
struct DLinkNode{
ElemType data; DLinkNode *prior; DLinkNode *next;};
//定义双链表结点类型,prior指向前一个结点,next指向后一个结点
和单链表基本是一样的。
双链表和循环双链表基本运算的实现
这里会提供双链表和循环双链表基本运算的实现及代码。
建立双链表——头插法
整体建立双链表和整体建立单链表一样也有两种方法,即头插法和尾插法,大致思路还是一样的,都是在头结点之后插入新节点。
//头插法建双链表
void CreateListF(DLinkNode *&L,ElemType a[],int n){
DLinkNode *s;
L=(DLinkNode *)malloc(sizeof(DLinkNode)); L->prior=L->next=NULL;//创建头结点
for (int i=0;i<n;i++){
s=(DLinkNode *)malloc(sizeof(DLinkNode));//创建新结点
s->data=a[i]; s->next=L->next;//和单链表的头插法一样,将新结点插入到头结点之后
if (L->next!=NULL) L->next->prior=s;
//此时如果L有指向的后继结点,即插入之前不为空表,L指向的后继结点为上一个插入的结点
//根据头插法,我们当前插入的新节点应当是上一个插入结点的上一个结点
//于是需要修改:L->next->prior=s
//前面两个指向地修改相当于对插入结点后继部分指向的修改
//接下来就是对前驱部分的修改:即对L和s之间建立前驱后继的指向关系
L->next=s;s->prior=L;
}
}
整体建立循环链表比整体循环普通链表,就多一步修改最后一个结点的指针域:
//头插法建循环双链表
void CreateListF(DLinkNode *&L,ElemType a[],int n){
DLinkNode *s;
L=(DLinkNode *)malloc(sizeof(DLinkNode)); L->prior=L->next=NULL;//创建头结点
for (int i=0;i<n;i++){
s=(DLinkNode *)malloc(sizeof(DLinkNode));//创建新结点
s->data=a[i]; s->next=L->next;//和单链表的头插法一样,将新结点插入到头结点之后
if (L->next!=NULL) L->next->prior=s;
//此时如果L有指向的后继结点,即插入之前不为空表,L指向的后继结点为上一个插入的结点
//根据头插法,我们当前插入的新节点应当是上一个插入结点的上一个结点
//于是需要修改:L->next->prior=s
//前面两个指向地修改相当于对插入结点后继部分指向的修改
//接下来就是对前驱部分的修改:即对L和s之间建立前驱后继的指向关系
L->next=s;s->prior=L;
}s=L->next;
while (s->next!=NULL) s=s->next; s->next=L; L->prior=s;
//查找尾结点,将尾结点next域指向头结点,尾结点的prior域指向头结点
}
建立双链表——尾插法
尾插法的大体思路和单链表那里也类似,只需要在插入那里增加对prior域的修改即可。
//尾插法建双链表,r始终指向终端结点,开始时指向头结点
void CreateListR(DLinkNode *&L,ElemType a[],int n){
DLinkNode *s,*r;
L=(DLinkNode *)malloc(sizeof(DLinkNode)); L->prior=L->next=NULL; r=L;//创建头结点
for (int i=0;i<n;i++){
s=(DLinkNode *)malloc(sizeof(DLinkNode));//创建新结点
s->data=a[i]; r->next=s;s->prior=r; //将结点s插入终端结点r之后
r=s;//依旧是非常关键地修改终端结点的一步
}r->next=NULL;//尾结点next域置空
}
循环双链表:
//尾插法建循环双链表,r始终指向终端结点,开始时指向头结点
void CreateListR(DLinkNode *&L,ElemType a[],int n){
DLinkNode *s,*r;
L=(DLinkNode *)malloc(sizeof(DLinkNode)); L->prior=L->next=NULL; r=L;//创建头结点
for (int i=0;i<n;i++){
s=(DLinkNode *)malloc(sizeof(DLinkNode));//创建新结点
s->data=a[i]; r->next=s;s->prior=r; //将结点s插入终端结点r之后
r=s;//依旧是非常关键地修改终端结点的一步
}r->next=L; L->prior=r;//将尾结点next域指向头结点,尾结点的prior域指向头结点
}
初始化线性表
写法和单链表那里一样,就是多一个prior域的置空(指向L):
//初始化线性表,初始化的线性表只有一个结点,prior和next域置空
void InitList(DLinkNode*& L){
L=(DLinkNode*)malloc(sizeof(DLinkNode));L->prior=L->next=NULL;}
循环双链表就是需要指向自己:
//初始化线性表,初始化的线性表只有一个结点
//循环双表最后一个结点需要指向第一个结点,头结点的前驱结点指向尾结点
void InitList(DLinkNode*& L){
L=(DLinkNode*)malloc(sizeof(DLinkNode));L->prior=L->next=L;}
销毁线性表
销毁线性表和单链表那里是完全一样的(注意是完全一样,没有任何区别):
//销毁线性表
void DestroyList(DLinkNode*& L){
DLinkNode *p=L,*q=p->next;
//考虑到前面提出的问题,我们需要用q提前存放下一个结点
while (q!=NULL){
//判断p是否为尾结点,如果p不是尾节点,则需要更新它后面的结点
free(p); p=q; q=p->next;
}free(p);//如果是尾结点,则直接销毁
}
循环双链表:
//销毁线性表
void DestroyList(DLinkNode*& L){
DLinkNode *p=L,*q=p->next;
//考虑到前面提出的问题,我们需要用q提前存放下一个结点
while (q!=L){
//判断p是否为尾结点,如果p不是尾节点,则需要更新它后面的结点
free(p); p=q; q=p->next;
}free(p);//如果是尾结点,则直接销毁
}
判断线性表是否为空
也是一样的:
//判断线性表是否为空
bool ListEmpty(DLinkNode* L){
return(L->next==NULL);}
循环双链表:
//判断线性表是否为空
bool ListEmpty(DLinkNode* L){
return(L->next==L);}
求线性表的长度
还是一样的······:
//求线性表的长度
int ListLength(DLinkNode* L){
DLinkNode *p=L; int len=0;
//一边从头结点一个一个往后遍历,一边用len计数,遍历到尾结点时,len即为链表长度
<