王道数据结构——双链表、循环双链表实现

ADT抽象数据类型

ADT 双链表(DoubleLinkList)
Data
	同线性表。元素具有相同的类型,相邻的元素具有前驱和后继关系。

Operation
	InitDLinkList(DLinkList &L);//初始化双链表
	CreateDList(DLinkList &L);//尾插法创建双链表
	PrintByOrderASC(DLinkList &L);//顺序打印双链表
	PrintByOrderDESC(DLinkList &L);//逆序打印双链表
	InsertByNextNode(Dnode *p,Dnode *s);//双链表的后插
	DListInsert(DLinkList &L, int i,ElemType e);//双链表的插入
	DeleteNextDnode(Dnode *p);//删除双链表的下一个结点
	DListDelete(DLinkList &L,int i,ElemType &e);//双链表的删除
	DestoryDList(DLinkList &L);//双链表的销毁
	

endADT

双链表的结构体定义

/* 相比单链表,也就多了一个头指针而已*/
typedef int ElemType;

typedef struct Dnode{
	ElemType data;//数据域
	struct Dnode * prior; //前驱指向 
	struct Dnode * next; //后驱指向 
}Dnode,*DLinkList;

双链表的初始化

/**
	初始化双链表 
*/

bool InitDLinkList(DLinkList &L) {
	L=(Dnode *)malloc(sizeof(Dnode)) ;//申请一个头结点 
	//初始化内存失败 
	if(L==NULL)return false;
	
	L->prior=NULL;//头结点的prior 永远指向NULL 
	L->next=NULL;
	
	return true;
}

双链表的创建——尾插法

尾插法和头插法与单链表的处理差不多,唯一的区别在于双链表多了一个头指针处理。头插法就不给出具体代码实现了。

/* 尾插法创建双链表 */
DLinkList CreateDList(DLinkList &L) {
	L=(Dnode *)malloc(sizeof(Dnode));//申请第一个结点 
	if(L!=NULL){
		L->prior=NULL; 
		L->next=NULL;
	}
	Dnode *s, *r=L;
	int x;
	cin>>x;
	while(x!=9999){
		s=(Dnode *)malloc(sizeof(Dnode));
		s->data=x;
		r->next=s;
		s->prior=r;//相比单链表,就多了一个指向r的前指针指向 
		r=s;
		cin>>x;
	}
	r->next=NULL;//最后一个结点指向为空
	
	return L; 
}

打印双链表

/*顺序打印双链表,跟单链表是一样的*/
void PrintByOrderASC(DLinkList &L){
	Dnode *p=L->next;//从第一个结点开始输出 
	while(p){//这样输出,导致当前的p可能为NULL 
		
		cout<<p->data<<" ";
		p=p->next;
	}
	cout<<endl;
}

/*逆序打印双链表,扫描到最后一个结点,再利用前驱结点倒回去*/
void PrintByOrderDESC(DLinkList &L){
	Dnode *p=L;
	while(p->next!=NULL){
		p=p->next;
	}
	
	while(p!=L){
		cout<<p->data<<" ";
		p=p->prior;
	}
	cout<<endl;
}

按值查找,按位查找,返回双链表的长度与单链表没有差别,这里也不给出具体的代码实现了。

双链表的后插

/**
	双链表的后插 
	如果p结点是最后一个结点,则需要增加判断条件之后再执行3 
	以下四步,如果顺序乱了,会出现断链或者自己指向自己的情况
	比如1,4变成了4,1 
	 
*/
bool InsertByNextNode(Dnode *p,Dnode *s){
	
	if(p==NULL||s==NULL) return false;
	//1.结点s的后继指向为p的后继结点 
	s->next=p->next;
	//2.p的后继结点的前指向修改为s 
	if(p->next!=NULL)//当p不是最后一个结点时
		p->next->prior=s;
	//3.s的前指向更改为p 
	s->prior=p;
	//4.p的后继指向更改为s 
	p->next=s;
	
	return true;
}

双链表的插入

/*前插,找到i-1,也可以后插*/
bool DListInsert(DLinkList &L, int i,ElemType e){
	if(i<1)	{
		cout<<"插入的位置有误!"<<endl;
		return false;
	}
	
	Dnode *p=L,*s;//从头结点开始遍历 
	int j=0;//从头结点开始遍历 
	while(p!=NULL&&j<i-1){
		p=p->next;
		j++;
	}
	
	s=(Dnode *)malloc(sizeof(Dnode)) ;
	s->data=e;
	InsertByNextNode(p,s);
}

删除双链表的下一个节点

/*删除双链表的下一个结点*/
bool DeleteNextDnode(Dnode *p){
	if(p==NULL)return false;

	Dnode *q=p->next;//q为p的后继结点
	if(q==NULL)//如果q为空,即p为双链表的最后一个结点
		return false;
		
	//不为空则将p的后继结点更改为q的后继结点	
	p->next=q->next;
	if(q->next!=NULL) //如果q不为最后一个结点
		q->next->prior=p;// q的后继结点的前驱指向p结点 
		
	free(q) ;//释放q结点

	return true; 	
}

双链表的删除

/*删除双链表的元素,并用e带回*/
bool DListDelete(DLinkList &L,int i,ElemType &e){
	if(i<1)	{
		cout<<"删除结点有误!" <<endl;
		return false;
	}
	Dnode *p=L;
	int j=0;//从头结点开始定位
	while(p!=NULL&&j<i-1) {
		p=p->next;
		j++;
	}
	
	//DeleteNextDnode(p);
	if(p==NULL)return false;//链表长为5,用户删除第7个元素,如果从第六个往后都为空 

	Dnode *q=p->next;//当前定位的结点,所要删除的结点 
	if(q==NULL)//如果q为空,即p为双链表的最后一个结点
		return false;
		
	//不为空则将p的后继结点更改为q的后继结点	
	p->next=q->next;
	if(q->next!=NULL) //如果q不为最后一个结点
		q->next->prior=p;// q的后继结点的前驱指向p结点 
	
	e=q->data;//带回所要删除的对象数据	
	free(q) ;//释放q结点
	return true;
	
}

双链表的销毁

/* 销毁双链表 */
void DestoryDList(DLinkList &L){
	while(L->next!=NULL){
		DeleteNextDnode(L);
	}
	free(L)	;//释放头结点 
	L=NULL;//头指针指向NULL 
}

循环双链表的初始化

/*初始化循环双链表*/
bool InitiDList(DLinkList &L){
	L=(Dnode *)malloc(sizeof(Dnode));
	if(L==NULL)return false;//初始化空间失败
	
	L->prior=L;//头指针指向自己 
	L->next=L;//尾指针指向自己
	//初始化,头尾指针都指向自己,自己抱着自己 
	return true;
}

循环双链表判空

/*判断循环双链表是否为空*/
bool IsEmpty(DLinkList &L){
	return (L->next==L)?true:false;
}

循环双链表判断表尾结点

/*判断节点p是否为循环双链表的表尾节点*/ 
bool isTrail(DLinkList &L, Dnode *p){
	return(p->next==L)?true:false;
}

后插

bool InsertByNextNode(Dnode *p,Dnode *s){
	

	//1.结点s的后继指向为p的后继结点 
	s->next=p->next;
	//2.p的后继结点的前指向修改为s 

	p->next->prior=s;
	//3.s的前指向更改为p 
	s->prior=p;
	//4.p的后继指向更改为s 
	p->next=s;
	
	return true;
}

删除下一个节点

/*删除循环双链表的下一个结点*/
bool DeleteNextDnode(Dnode *p){
	if(p==NULL)return false;
	Dnode *q=p->next;//q为p的后继结点
	//不为空则将p的后继结点更改为q的后继结点	
	p->next=q->next;
	q->next->prior=p;// q的后继结点的前驱指向p结点 
	free(q) ;//释放q结点
	return true; 	
}

后插和删除下一个节点在双链表和循环双链表之间区别在于循环双链表不需要判断后继节点是否为空,其他的一致。由于双链表跟循环双链表之间的区别,仅仅在于尾指针的指向是否是L,其它的代码实现方式跟双链表相差不多,仅在于尾指针的处理,所以,下面只给出了双链表的具体实现。


代码实现—双链表

#include<iostream>
#include<stdlib.h> 
using namespace std;

typedef int ElemType;

typedef struct Dnode{
	ElemType data;//数据域
	struct Dnode * prior; //前驱指向 
	struct Dnode * next; //后驱指向 
}Dnode,*DLinkList;


/**
	初始化双链表 
*/

bool InitDLinkList(DLinkList &L) {
	L=(Dnode *)malloc(sizeof(Dnode)) ;//申请一个头结点 
	//初始化内存失败 
	if(L==NULL)return false;
	
	L->prior=NULL;//头结点的prior 永远指向NULL 
	L->next=NULL;
	
	return true;
}



/**
	双链表的插入 
	如果p结点是最后一个结点,则需要增加判断条件之后再执行3 
	以下四步,如果顺序乱了,会出现断链或者自己指向自己的情况
	比如1,4变成了4,1 
	 
*/
bool InsertByNextNode(Dnode *p,Dnode *s){
	
	if(p==NULL||s==NULL) return false;
	//1.结点s的后继指向为p的后继结点 
	s->next=p->next;
	//2.p的后继结点的前指向修改为s 
	if(p->next!=NULL)//当p不是最后一个结点时
		p->next->prior=s;
	//3.s的前指向更改为p 
	s->prior=p;
	//4.p的后继指向更改为s 
	p->next=s;
	
	return true;
}

/*
	s->next = p->next;
	p->next->prior = s;//要判断当前的p结点是不是最后一个结点 
	p->next = s;
	s->prior = p;
*/


/*前插,找到i-1,也可以后插*/
bool DListInsert(DLinkList &L, int i,ElemType e){
	if(i<1)	{
		cout<<"插入的位置有误!"<<endl;
		return false;
	}
	
	Dnode *p=L,*s;//从头结点开始遍历 
	int j=0;//从头结点开始遍历 
	while(p!=NULL&&j<i-1){
		p=p->next;
		j++;
	}
	
	s=(Dnode *)malloc(sizeof(Dnode)) ;
	s->data=e;
	InsertByNextNode(p,s);
}


/* 尾插法创建双链表 */
DLinkList CreateDList(DLinkList &L) {
	L=(Dnode *)malloc(sizeof(Dnode));//申请第一个结点 
	if(L!=NULL){
		L->prior=NULL; 
		L->next=NULL;
	}
	Dnode *s, *r=L;
	int x;
	cin>>x;
	while(x!=9999){
		s=(Dnode *)malloc(sizeof(Dnode));
		s->data=x;
		r->next=s;
		s->prior=r;//相比单链表,就多了一个指向r的前指针指向 
		r=s;
		cin>>x;
	}
	r->next=NULL;//最后一个结点指向为空
	
	return L; 
}

/*顺序打印双链表,跟单链表是一样的*/
void PrintByOrderASC(DLinkList &L){
	Dnode *p=L->next;//从第一个结点开始输出 
	while(p){//这样输出,导致当前的p可能为NULL 
		
		cout<<p->data<<" ";
		p=p->next;
	}
	cout<<endl;
}

/*逆序打印双链表,扫描到最后一个结点,再利用前驱结点倒回去*/
void PrintByOrderDESC(DLinkList &L){
	Dnode *p=L;
	while(p->next!=NULL){
		p=p->next;
	}
	
	while(p!=L){
		cout<<p->data<<" ";
		p=p->prior;
	}
	cout<<endl;
}

/*删除双链表的下一个结点*/
bool DeleteNextDnode(Dnode *p){
	if(p==NULL)return false;

	Dnode *q=p->next;//q为p的后继结点
	if(q==NULL)//如果q为空,即p为双链表的最后一个结点
		return false;
		
	//不为空则将p的后继结点更改为q的后继结点	
	p->next=q->next;
	if(q->next!=NULL) //如果q不为最后一个结点
		q->next->prior=p;// q的后继结点的前驱指向p结点 
		
	free(q) ;//释放q结点

	return true; 	
}

/* 销毁双链表 */
void DestoryDList(DLinkList &L){
	while(L->next!=NULL){
		DeleteNextDnode(L);
	}
	free(L)	;//释放头结点 
	L=NULL;//头指针指向NULL 
}

/*删除双链表的元素,并用e带回*/
bool DListDelete(DLinkList &L,int i,ElemType &e){
	if(i<1)	{
		cout<<"删除结点有误!" <<endl;
		return false;
	}
	Dnode *p=L;
	int j=0;//从头结点开始定位
	while(p!=NULL&&j<i-1) {
		p=p->next;
		j++;
	}
	
	//DeleteNextDnode(p);
	if(p==NULL)return false;//链表长为5,用户删除第7个元素,如果从第六个往后都为空 

	Dnode *q=p->next;//当前定位的结点,所要删除的结点 
	if(q==NULL)//如果q为空,即p为双链表的最后一个结点
		return false;
		
	//不为空则将p的后继结点更改为q的后继结点	
	p->next=q->next;
	if(q->next!=NULL) //如果q不为最后一个结点
		q->next->prior=p;// q的后继结点的前驱指向p结点 
	
	e=q->data;//带回所要删除的对象数据	
	free(q) ;//释放q结点
	return true;
	
}

int main(){
	DLinkList L;
	InitDLinkList(L);
	CreateDList(L);
	PrintByOrderASC(L);
	
	cout<<"在第3个位置插入3:";
	DListInsert(L,3,3);
	PrintByOrderASC(L);
	
	int e;
	DListDelete(L,5,e);
	cout<<"删除第五个结点,第五个结点的值为:"<<e<<endl;
	cout<<"顺序输出最后结果为:";
	PrintByOrderASC(L);
	cout<<"逆序输出最后结果为:";
	PrintByOrderDESC(L);
	
}


运行结果:
1 2 3 4 5 9999
1 2 3 4 5
在第3个位置插入3:1 2 3 3 4 5
删除第五个结点,第五个结点的值为:4
顺序输出最后结果为:1 2 3 3 5
逆序输出最后结果为:5 3 3 2 1
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值