【数据结构】C语言实现双向循环链表

双向循环链表基本操作

在这里插入图片描述

存储结构类型定义

typedef struct DuLNode
{
	ElemType data;   //数据域 
	struct DuLNode *prior;   //直接前驱 
	struct DuLNode *next;	 //直接后继 
}DuLNode,*DuLinkList;

头插法

//头插法
void CreateList_H(DuLinkList *LHead,int n)
{
	int i;
	*LHead = (DuLinkList)malloc(sizeof(DuLNode));   //头结点
	DuLNode * LTail = (DuLinkList)malloc(sizeof(DuLNode));   //尾结点 
	DuLNode *tmp_node;    //临时结点(新插入结点)    
	(*LHead)->next = LTail;   //头结点next指向尾结点
	(*LHead)->prior = LTail;   //头结点prior指向尾结点 
	LTail->next = *LHead;      //尾结点next指向头结点 
	LTail->prior = *LHead;     //尾结点指向头结点  进而首位相连形成一个双向循环 
	printf("头插法输入%d个元素: ",n);
	scanf("%d",&LTail->data);    //循环外为尾结点输入数据先 
	 for(i=0;i<n-1;i++)
	 {
	 	tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //动态分配新节点空间单元 
	 	scanf("%d",&tmp_node->data);           // 为新结点输入数据 
	 	tmp_node->next = (*LHead)->next;       //新结点next指向后继结点
	 	tmp_node->prior = (*LHead)->next->prior;   //尾结点一直都在动,不能用之作为参考点,所以用头结点LHead指向自己 
//	 	(*LHead)->next = tmp_node;      //头结点next指针重置,指向新结点 
	 	(*LHead)->next->prior = tmp_node;   //原后继结点prior(使用固定的头结点表示)重置,指向新结点 
	 	(*LHead)->next = tmp_node;      //此处做个致命错误标志,这两步不能乱 
	 }
}

注意插入元素时指针域修改的先后顺序,例如:这里的倒数两行代码,先修改(*LHead)->next->prior = tmp_node; 再修改(*LHead)->next = tmp_node; 它们之间的关系prior指针需要头结点原来的next获得,如果先改了next就将出现指向错误和断链的现象。

尾插法

//尾插法
void CreateList_R(DuLinkList *LHead,int n)
{
	int i;
	*LHead = (DuLinkList)malloc(sizeof(DuLNode));   //开辟头结点内存单元 
	DuLNode *tmp_node;     //定义一个临时结点指针(新插入结点指针) 
	(*LHead)->next = *LHead;   //头结点next指向自己 
	(*LHead)->prior = *LHead;    //头结点prior指向自己 
	printf("尾插法输入%d个元素: ",n);
	for(i=0;i<n;i++)
	{
		tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //为新结点动态开辟空间,让新结点指针指向它 
		tmp_node->next = *LHead;    //新插入结点next与头结点进行连接 
		tmp_node->prior = (*LHead)->prior;    //新插入结点prior与原链尾连接   这两步其实就是将新插入结点的next和prior都连接到链尾 
		scanf("%d",&tmp_node->data);    //为新结点输入数据 
		(*LHead)->prior->next = tmp_node;    //使用头结点prior引出原链尾结点,并让它next指向新结点 
		(*LHead)->prior = tmp_node;       //头结点prior指向新结点 
	}
}

遍历

//遍历
Status print(DuLinkList LHead)
{
	DuLNode *tmp_node = LHead;  //一开始指向头结点 
	if(LHead->next == LHead)  return ERROR;   //空链表返回ERROR
	printf("当前链表:"); 
	while(LHead != tmp_node->next)  //尾结点的next域不等于头结点指针时循环继续 
	{
		tmp_node = tmp_node->next;      //循环体内每移动一项立即输出data,然后再判断就不会漏了 
		printf("%d ",tmp_node->data);
	}
	printf("\n");
	return OK;
}

链表合并

//合并  A链合并到B链 
Status Union(DuLinkList *LHead_a,DuLinkList *LHead_b)
{
	if(((*LHead_a)->next == *LHead_a) || ((*LHead_b)->next == *LHead_b)) return ERROR;
	(*LHead_a)->prior->next = (*LHead_b)->next;   //A链链尾next指向B链首元结点
	(*LHead_b)->next->prior = (*LHead_a)->prior;     //B链链头prior与A链链尾相连 
	(*LHead_a)->prior = (*LHead_b)->prior;   //A链头结点prior与B链链尾相连 
	(*LHead_b)->prior->next = *LHead_a;  //B链尾结点next指向A链头结点 
	free(*LHead_b);   //释放B链头结点内存单元 
	free(LHead_b);     //释放B链头指针 
}

插入结点

图解
在这里插入图片描述

头插
//插入(头插法)
void ListInsert_H(DuLinkList *LHead,ElemType e)     //LHead头指针 指针 
{
	DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //新结点储存单元空间指针 
	tmp_node->data = e;     //新结点输入数据 
	tmp_node->next = (*LHead)->next;      //新结点next指向头结点指向的下一个结点 
	tmp_node->prior = (*LHead)->next->prior;     //新结点prior指向头结点(也可以把*LHead->next->prior替换为*LHead)
	(*LHead)->next->prior = tmp_node;     //将原首元结点的prior指向换为新结点 
	(*LHead)->next = tmp_node;        //将头结点next指向新结点
	printf("元素%d 成功插头~\n",e); 
}
尾插
//插入(尾插法)
void ListInsert_R(DuLinkList *LHead,ElemType e)    //LHead头指针 指针 
{
	DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //开辟空间单元给新结点 
	tmp_node->data = e;      //为新结点输入数据 
	tmp_node->next = *LHead;       //新结点next指向头结点 
	tmp_node->prior = (*LHead)->prior;    //新结点prior指向上一个元素(使用头结点的prior替代) 
	(*LHead)->prior->next = tmp_node;    //原链尾next指向新结点 
	(*LHead)->prior = tmp_node;          //头结点prior指向新结点
	printf("元素%d 成功插尾~\n",e); 
}
中间插
//插入到表中间 
Status ListInsert_C(DuLinkList LHead,int n,ElemType e)
{
	DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //新界结点指针并为之开辟空间 
	DuLNode *p = GetElem(LHead,n);           ///按位序找指针 
//	printf("%d",p->data);
	if(!p) return ERROR;          //如果返回指针有误就结束插入操作 
	tmp_node->data = e;              //新结点输入数据 
	tmp_node->next = p;              //新结点next指针指向查找原位置指针p 
	tmp_node->prior = p->prior;     //新结点prior指向原位置结点的prior的指针 
	p->prior->next = tmp_node;        //原位p结点的上一个结点的next指向新结点 
	p->prior = tmp_node;                //原位置结点的prior指向新结点 
	printf("元素%d 成功插入中间~\n",e);
	return OK;
}
整合到同一个函数

实现一个函数插三个位置。

//插入  整合了头尾中间 
Status ListInsert(DuLinkList *LHead,int n,ElemType e)
{
	DuLNode *tmp_node;                 //新结点指针 
	if(n==1) ListInsert_H(LHead,e);    //首先判断插入位置为1则是查到链头,所以用插头ListInsert_H 
	tmp_node = GetElem(*LHead,n);       //按位找该位置结点指针  
	if(!tmp_node) return ERROR;       //返回结点有误则结束 
	if(tmp_node->next == *LHead) ListInsert_R(LHead,e);       //如果该位置是链尾直接用插尾函数插入 
	ListInsert_C(*LHead,n,e);     //‘剩下的就是插中间 
	return OK;   
}

按结点位置查找

//按位查找
DuLNode *GetElem(DuLinkList LHead,int n)
{
	int j=1;  //这里从首元结点开始查找,j设置为1 
	DuLNode *tmp_node = LHead->next;  //指向首元结点
	while(tmp_node!=LHead && j<n)    //循环条件:链表非空——next域不指向自己,取值位置合理 
	{
		tmp_node = tmp_node->next;   //指针指向下一个 
		j++;   //位置计数加一 
	}
	if(tmp_node==LHead && j>=n) return ERROR;   //空链以及取值位置不合理返回ERROR 
	return tmp_node;   //返回查找位置 指针 
}

删除

//删除 
Status ListDetele(DuLinkList *LHead,int n)
{
	int i;
	DuLNode *tmp_node;
	tmp_node = (*LHead)->next;  //指向首元结点
	for(i=1;i<n;i++)
		tmp_node = tmp_node->next;         //for循环遍历查找第n个结点 
	tmp_node->next->prior = tmp_node->prior;    //第n个结点后一个的prior指向第n前一个结点指针 
	tmp_node->prior->next = tmp_node->next;        //同理,第n个前一个next指针指向第n个结点后一个 
	free(tmp_node);    //释放第n结点的内存单元空间 
	printf("成功删除位置%d的元素~\n",n); 
}

代码整合进行测试

#include <stdio.h>
#include <stdlib.h>

#define OK 1
#define ERROR 0

typedef int Status;
typedef int ElemType;

typedef struct DuLNode
{
	ElemType data;   //数据域 
	struct DuLNode *prior;   //直接前驱 
	struct DuLNode *next;	 //直接后继 
}DuLNode,*DuLinkList;

//头插法
void CreateList_H(DuLinkList *LHead,int n)
{
	int i;
	*LHead = (DuLinkList)malloc(sizeof(DuLNode));   //头结点
	DuLNode * LTail = (DuLinkList)malloc(sizeof(DuLNode));   //尾结点 
	DuLNode *tmp_node;    //临时结点(新插入结点)    
	(*LHead)->next = LTail;   //头结点next指向尾结点
	(*LHead)->prior = LTail;   //头结点prior指向尾结点 
	LTail->next = *LHead;      //尾结点next指向头结点 
	LTail->prior = *LHead;     //尾结点指向头结点  进而首位相连形成一个双向循环 
	printf("头插法输入%d个元素: ",n);
	scanf("%d",&LTail->data);    //循环外为尾结点输入数据先 
	 for(i=0;i<n-1;i++)
	 {
	 	tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //动态分配新节点空间单元 
	 	scanf("%d",&tmp_node->data);           // 为新结点输入数据 
	 	tmp_node->next = (*LHead)->next;       //新结点next指向后继结点
	 	tmp_node->prior = (*LHead)->next->prior;   //尾结点一直都在动,不能用之作为参考点,所以用头结点LHead指向自己 
//	 	(*LHead)->next = tmp_node;      //头结点next指针重置,指向新结点 
	 	(*LHead)->next->prior = tmp_node;   //原后继结点prior(使用固定的头结点表示)重置,指向新结点 
	 	(*LHead)->next = tmp_node;      //此处做个致命错误标志,这两步不能乱 
	 }
}

//尾插法
void CreateList_R(DuLinkList *LHead,int n)
{
	int i;
	*LHead = (DuLinkList)malloc(sizeof(DuLNode));   //开辟头结点内存单元 
	DuLNode *tmp_node;     //定义一个临时结点指针(新插入结点指针) 
	(*LHead)->next = *LHead;   //头结点next指向自己 
	(*LHead)->prior = *LHead;    //头结点prior指向自己 
	printf("尾插法输入%d个元素: ",n);
	for(i=0;i<n;i++)
	{
		tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //为新结点动态开辟空间,让新结点指针指向它 
		tmp_node->next = *LHead;    //新插入结点next与头结点进行连接 
		tmp_node->prior = (*LHead)->prior;    //新插入结点prior与原链尾连接   这两步其实就是将新插入结点的next和prior都连接到链尾 
		scanf("%d",&tmp_node->data);    //为新结点输入数据 
		(*LHead)->prior->next = tmp_node;    //使用头结点prior引出原链尾结点,并让它next指向新结点 
		(*LHead)->prior = tmp_node;       //头结点prior指向新结点 
	}
}

//遍历
Status print(DuLinkList LHead)
{
	DuLNode *tmp_node = LHead;  //一开始指向头结点 
	if(LHead->next == LHead)  return ERROR;   //空链表返回ERROR
	printf("当前链表:"); 
	while(LHead != tmp_node->next)  //尾结点的next域不等于头结点指针时循环继续 
	{
		tmp_node = tmp_node->next;      //循环体内每移动一项立即输出data,然后再判断就不会漏了 
		printf("%d ",tmp_node->data);
	}
	printf("\n");
	return OK;
}

//按位查找
DuLNode *GetElem(DuLinkList LHead,int n)
{
	int j=1;  //这里从首元结点开始查找,j设置为1 
	DuLNode *tmp_node = LHead->next;  //指向首元结点
	while(tmp_node!=LHead && j<n)    //循环条件:链表非空——next域不指向自己,取值位置合理 
	{
		tmp_node = tmp_node->next;   //指针指向下一个 
		j++;   //位置计数加一 
	}
	if(tmp_node==LHead && j>=n) return ERROR;   //空链以及取值位置不合理返回ERROR 
	return tmp_node;   //返回查找位置 指针 
}


//插入(尾插法)
void ListInsert_R(DuLinkList *LHead,ElemType e)    //LHead头指针 指针 
{
	DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //开辟空间单元给新结点 
	tmp_node->data = e;      //为新结点输入数据 
	tmp_node->next = *LHead;       //新结点next指向头结点 
	tmp_node->prior = (*LHead)->prior;    //新结点prior指向上一个元素(使用头结点的prior替代) 
	(*LHead)->prior->next = tmp_node;    //原链尾next指向新结点 
	(*LHead)->prior = tmp_node;          //头结点prior指向新结点
	printf("元素%d 成功插尾~\n",e); 
}

//插入(头插法)
void ListInsert_H(DuLinkList *LHead,ElemType e)     //LHead头指针 指针 
{
	DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //新结点储存单元空间指针 
	tmp_node->data = e;     //新结点输入数据 
	tmp_node->next = (*LHead)->next;      //新结点next指向头结点指向的下一个结点 
	tmp_node->prior = (*LHead)->next->prior;     //新结点prior指向头结点(也可以把*LHead->next->prior替换为*LHead)
	(*LHead)->next->prior = tmp_node;     //将原首元结点的prior指向换为新结点 
	(*LHead)->next = tmp_node;        //将头结点next指向新结点
	printf("元素%d 成功插头~\n",e); 
}

//插入到表中间 
Status ListInsert_C(DuLinkList LHead,int n,ElemType e)
{
	DuLNode *tmp_node = (DuLinkList)malloc(sizeof(DuLNode));    //新界结点指针并为之开辟空间 
	DuLNode *p = GetElem(LHead,n);           ///按位序找指针 
//	printf("%d",p->data);
	if(!p) return ERROR;          //如果返回指针有误就结束插入操作 
	tmp_node->data = e;              //新结点输入数据 
	tmp_node->next = p;              //新结点next指针指向查找原位置指针p 
	tmp_node->prior = p->prior;     //新结点prior指向原位置结点的prior的指针 
	p->prior->next = tmp_node;        //原位p结点的上一个结点的next指向新结点 
	p->prior = tmp_node;                //原位置结点的prior指向新结点 
	printf("元素%d 成功插入中间~\n",e);
	return OK;
}

//插入  整合了头尾中间 
Status ListInsert(DuLinkList *LHead,int n,ElemType e)
{
	DuLNode *tmp_node;                 //新结点指针 
	if(n==1) ListInsert_H(LHead,e);    //首先判断插入位置为1则是查到链头,所以用插头ListInsert_H 
	tmp_node = GetElem(*LHead,n);       //按位找该位置结点指针  
	if(!tmp_node) return ERROR;       //返回结点有误则结束 
	if(tmp_node->next == *LHead) ListInsert_R(LHead,e);       //如果该位置是链尾直接用插尾函数插入 
	ListInsert_C(*LHead,n,e);     //‘剩下的就是插中间 
	return OK;   
}

//删除 
Status ListDetele(DuLinkList *LHead,int n)
{
	int i;
	DuLNode *tmp_node;
	tmp_node = (*LHead)->next;  //指向首元结点
	for(i=1;i<n;i++)
		tmp_node = tmp_node->next;         //for循环遍历查找第n个结点 
	tmp_node->next->prior = tmp_node->prior;    //第n个结点后一个的prior指向第n前一个结点指针 
	tmp_node->prior->next = tmp_node->next;        //同理,第n个前一个next指针指向第n个结点后一个 
	free(tmp_node);    //释放第n结点的内存单元空间 
	printf("成功删除位置%d的元素~\n",n); 
}

//合并  A链合并到B链 
Status Union(DuLinkList *LHead_a,DuLinkList *LHead_b)
{
	if(((*LHead_a)->next == *LHead_a) || ((*LHead_b)->next == *LHead_b)) return ERROR;
	(*LHead_a)->prior->next = (*LHead_b)->next;   //A链链尾next指向B链首元结点
	(*LHead_b)->next->prior = (*LHead_a)->prior;     //B链链头prior与A链链尾相连 
	(*LHead_a)->prior = (*LHead_b)->prior;   //A链头结点prior与B链链尾相连 
	(*LHead_b)->prior->next = *LHead_a;  //B链尾结点next指向A链头结点 
	free(*LHead_b);   //释放B链头结点内存单元 
	free(LHead_b);     //释放B链头指针 
}
 
//测试
int main()
{
	DuLinkList LHead_a,LHead_b;   //定义两条链的头指针 
	printf("------头插法---------\n");
	CreateList_H(&LHead_a,3);
	print(LHead_a);
	printf("\n------尾插法--------\n");
	CreateList_R(&LHead_b,3);
	print(LHead_b);
	printf("\n------链表合并------\n");
	Union(&LHead_a,&LHead_b);
	print(LHead_a);
	printf("\n---头插法插入 10---\n");
	ListInsert_H(&LHead_a,10);
	print(LHead_a);
	printf("\n---尾插法插 5---\n");
	ListInsert_R(&LHead_a,5);
	print(LHead_a);
	printf("\n------按位查找------\n");
	DuLNode *p = GetElem(LHead_a,2);
	printf("第二个结点数据: %d\n",p->data);
	printf("\n---插入 100 到链表中间---\n");
	ListInsert(&LHead_a,4,100);
	print(LHead_a); 
	printf("\n-----链表删除--------\n");
	ListDetele(&LHead_a,2);
	print(LHead_a); 
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值