第二章——双链表和循环双链表

线性表——双链表和循环双链表

双链表的定义在链表那里就介绍过了,这里就不重复介绍了。


双链表和循环双链表

同样地,我们用结构体来声明链表中的一个结点:

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即为链表长度 
	<
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值