双向循环链表的基本操作

双向循环链表的基本操作

双向循环链表介绍

双向链表的结点:指针域1+数据域+指针域2

指针域1:指向该结点前面一个结点

指针域2:指向该结点后面一个结点
在这里插入图片描述

头文件

#include<iostream>
#include<assert.h>
using std::cin;
using std::cout;
using std::endl;

双向链表的基本结构

typedef int Datatype;  //声明数据类型

typedef struct ListNode
{
	Datatype data;      //存储数据
	struct ListNode* next;   //指向下一个节点
	struct ListNode* prev;   //指向上一个节点
}ListNode, * pListNode;

首先声明数据类型为Datatype,在接下来双链表的基本操作中,将用Datatype代表int。
如果后续存储的数据类型需要改变为其他的(如: float,double等)
只需要修改:typedef int Datatype;大大减少了工作量

next记录后继节点,prev记录前驱节点

将结构体重命名为ListNode和 * pListNode,后续声明该结构体指针时,无需用 struct ListNode * p,可以直接pListNode p,
p就为该结构体的指针
(本文后续将采用此中方法)

初始化双链表

在这里插入图片描述
初始化链表,即malloc一个哨兵节点,该节点的next指向自己,prev也指向自己,且哨兵节点中不存储值

ListNode* Init_List()
{
	pListNode s = (pListNode)malloc(sizeof(ListNode));
	if (!s) //判断malloc是否成功,若失败,判断成立,返回NULL
	{
		perror("Init_List_malloc_error!");
		return NULL;
	}
	s->next = s;
	s->prev = s;
	return s;
}

这里初始化哨兵节点,将哨兵节点的next和prev都指向自身,此时链表为空
顺便提一句: malloc可能会分配失败(但这种情况为少数),若分配失败则会返回NULL;这时 !s 的结果为 1,也就是malloc失败,执行报错提示,并返回NULL;

双链表判空

int Empty_List(pListNode L)
{
	if (!L)
	{
		perror("ListNode*_is_NULL");
		return -1;
	}
	return L->next == L;
}

若传入节点的next等于该节点本身,说明该双链表为空
L->next==L成立,返回1,否则不为空,返回0;

下图为只有一个哨兵位的双链表,即空表,判空条件有如下两种
在这里插入图片描述

双链表尾插

在这里插入图片描述

void Insert_End(ListNode* L, Datatype x)  
{
	if (!L)
	{
		perror("Inser_End_L_NULL");
		return;
	}
	pListNode NewNode = (ListNode*)malloc(sizeof(ListNode));
	if (!NewNode)
	{
		perror("Insert_End_malloc");
		return;
	}
	NewNode->data = x;
	NewNode->next = L;
	NewNode->prev = L->prev;
	L->prev->next = NewNode;
	L->prev = NewNode;
}

同样先检查传入的链表,以及malloc的节点是否为空,若没有问题,再将节点的值赋值为x,以及新节点前后指针的修改。

双链表头插

在这里插入图片描述

void Insert_Head(ListNode* L, Datatype x)   
{
	if (!L)
	{
		perror("Inser_Head_L_NULL");
		return;
	}
	pListNode NewNode  = (ListNode*)malloc(sizeof(ListNode));
	if (!NewNode)
	{
		perror("Insert_End_malloc");
		return;
	}
	NewNode->data = x;
	NewNode->next = L->next;
	NewNode->prev = L;
	L->next->prev = NewNode;
	L->next = NewNode;
}

创建一个新节点

pListNode Buy_newNode(Datatype x)
{
	pListNode newnode = (pListNode)malloc(sizeof(ListNode));
	if (!newnode)
	{
		perror("malloc_error!");
		return NULL;
	}
	newnode->data = x;
	newnode->prev = NULL;
	newnode->next = NULL;
	return newnode;
}

这个Buy_newNode函数用来动态分配一个新的ListNode节点,并检查是否创建成功。

有了这个函数,可以减少我们的工作量

比如:
之前在头插和尾插中,我们都是先malloc一个新节点,检查是否分配成功,并为这个NewNode赋值。

pListNode NewNode  = (ListNode*)malloc(sizeof(ListNode));
	if (!NewNode)
	{
		perror("Insert_End_malloc");
		return;
	}
	NewNode->data = x;

现在我们只需要

pListNode NewNode = Buy_newNode(x);

也就是说,我们的头插和尾插可以做如下优化

//尾插
void Insert_End(ListNode* L, Datatype x)  
{
	if (!L)
	{
		perror("Inser_End_L_NULL");
		return;
	}
	
	pListNode NewNode = Buy_newNode(x);
	
	/*
	pListNode NewNode = (ListNode*)malloc(sizeof(ListNode));
	if (!NewNode)
	{
		perror("Insert_End_malloc");
		return;
	}
	*/
	
	NewNode->data = x;
	NewNode->next = L;
	NewNode->prev = L->prev;
	L->prev->next = NewNode;
	L->prev = NewNode;
}


//头插
void Insert_Head(ListNode* L, Datatype x)   
{
	if (!L)
	{
		perror("Inser_Head_L_NULL");
		return;
	}
	
	pListNode NewNode = Buy_newNode(x);
	
	/*pListNode NewNode  = (ListNode*)malloc(sizeof(ListNode));
	if (!NewNode)
	{
		perror("Insert_End_malloc");
		return;
	}
	NewNode->data = x;
	*/
	NewNode->next = L->next;
	NewNode->prev = L;
	L->next->prev = NewNode;
	L->next = NewNode;
}

如此,不仅减少了工作量,也减轻了代码的冗余。

在某一节点前插入一个新节点

在这里插入图片描述

void Insert_List(pListNode pos, Datatype x)
{
	assert(pos);
	pListNode pos_prev = pos->prev;

	pListNode newNode = Buy_newNode(x);

	//pos_prev newNode pos
	pos_prev->next = newNode;
	newNode->prev = pos_prev;
	newNode->next = pos;
	pos->prev = newNode;
}

Insert_List函数能够创建一个新的节点,并为该节点赋值为x,同时将新创建的节点插入至节点L之前。

这个代码十分强劲,能够同时完成创建并插入。

看到这里的小伙伴不知有没有想到,之前的头插和尾插也是创建节点并赋值,再将新节点进行插入。

这两者的模式十分相同,那么我们是否又可以对之前的代码进行优化?

答案是肯定的。

看代码:

//尾插
void Insert_End(ListNode* L, Datatype x)  
{
	if (!L)
	{
		perror("Inser_End_L_NULL");
		return;
	}
	Insert_List ( L->prev,x );
}


//头插
void Insert_Head(ListNode* L, Datatype x)   
{
	if (!L)
	{
		perror("Inser_Head_L_NULL");
		return;
	}
	Insert_List ( L->next,x );
}

之前的两个庞然大物,现在直接变得精小细致。

删除当前节点

在这里插入图片描述

int Delete_(pListNode s) 
{
	if (!s)
	{
		perror("Delete_L_NULL");
		return 0;
	}
	s->prev->next = s->next;
	s->next->prev = s->prev;
	free(s);
	s = NULL;
	return 1;

该函数能够删除当前传入的指针所指向的节点。

双链表头删

void Delete_Head(pListNode L)
{
	assert(L);//判断当前指针是否为空指针
	assert(!Empty_List(L));  //判断链表是否为空
	Delete_(L->next);
}

assert排除完异常情况后,就可以用删除节点函数Delete_(s)

因为头删就是删除哨兵节点的下一个节点,只要调用Delete函数并传入L->next即可

双链表尾删

void Delete_End(pListNode L)
{
	assert(L);
	assert(!Empty_List(L));
	Delete_(L->prev);  //删除某一位置的元素
}

同理,尾删就是删除哨兵节点的前一个节点,传入L->prev即可

双链表打印

打印双链表中的元素

void Printf_List(pListNode L)   
{
	if (!L)
	{
		perror("Printf_L_NULL");
		return;
	}
	pListNode s = L->next;
	cout << "表中元素为:";
	while (s != L)
	{
		cout << s->data << " ";
		s = s->next;
	}
	cout << endl << "打印完毕!" << endl;
}

注意:循环停止的条件为当前节点的next节点如果等于哨兵节点,则说明链表已经全部遍历完,这里的等于指的是指针值相等,而不是节点中存储的数据值相等

查找

pListNode Find_List(pListNode L, Datatype x)   //按值查找
{
	if (!L)
	{
		perror("Find_L_NULL");
		return NULL;
	}
	pListNode s = L->next;
	while (s != L)
	{
		if (s->data == x)
		{
			return s;
		}
		else
		{
			s = s->next;
		}
	}
	if (s = L)
	{
		cout << "Can't find " << x << " in List!" << endl;
	}
	return NULL;
}

按值查找,若在链表中找到匹配的值,则返回该节点的地址,否则返回NULL;

销毁双链表

int Destory_List(pListNode L)  
{
	if (!L)
	{
		perror("Destory_L_NULL");
		return 0;
	}
	pListNode s = L->next, r = s;
	while (s != L)
	{
		s = s->next;
		free(r);
		r = s;
	}
	free(L);
	L = NULL;
	return 1;
}

先令指针s指向哨兵节点的后一个节点,依次释放节点,直至指针s与哨兵节点相等,最后别忘了释放头节点。
注意: 这里不能先释放哨兵节点再依次循环删除其他节点,即s必须指向哨兵节点的下一个节点,因为删除了哨兵节点,再循环,等到运行到链表最后一个节点时,s->next指向的空间已经释放了,运行会异常

代码总览

typedef int Datatype;  //声明数据类型

typedef struct ListNode
{
	Datatype data;      //存储数据
	struct ListNode* next;   //指向下一个节点
	struct ListNode* prev;   //指向上一个节点
}ListNode, * pListNode;

ListNode* Init_List()
{
	pListNode s = (pListNode)malloc(sizeof(ListNode));
	if (!s) //判断malloc是否成功,若失败,判断成立,返回NULL
	{
		perror("Init_List_malloc_error!");
		return NULL;
	}
	s->next = s;
	s->prev = s;
	return s;
}

int Empty_List(pListNode L)
{
	if (!L)
	{
		perror("ListNode*_is_NULL");
		return -1;
	}
	return L->next == L;
}

void Insert_End(ListNode* L, Datatype x)  
{
	if (!L)
	{
		perror("Inser_End_L_NULL");
		return;
	}
	pListNode NewNode = (ListNode*)malloc(sizeof(ListNode));
	if (!NewNode)
	{
		perror("Insert_End_malloc");
		return;
	}
	NewNode->data = x;
	NewNode->next = L;
	NewNode->prev = L->prev;
	L->prev->next = NewNode;
	L->prev = NewNode;
}

void Insert_Head(ListNode* L, Datatype x)   
{
	if (!L)
	{
		perror("Inser_Head_L_NULL");
		return;
	}
	pListNode NewNode  = (ListNode*)malloc(sizeof(ListNode));
	if (!NewNode)
	{
		perror("Insert_End_malloc");
		return;
	}
	NewNode->data = x;
	NewNode->next = L->next;
	NewNode->prev = L;
	L->next->prev = NewNode;
	L->next = NewNode;
}


//创建一个新节点
pListNode Buy_newNode(Datatype x)
{
	pListNode newnode = (pListNode)malloc(sizeof(ListNode));
	if (!newnode)
	{
		perror("malloc_error!");
		return NULL;
	}
	newnode->data = x;
	newnode->prev = NULL;
	newnode->next = NULL;
	return newnode;
}

void Insert_List(pListNode pos, Datatype x)
{
	assert(pos);
	pListNode pos_prev = pos->prev;

	pListNode newNode = Buy_newNode(x);

	//pos_prev newNode pos
	pos_prev->next = newNode;
	newNode->prev = pos_prev;
	newNode->next = pos;
	pos->prev = newNode;
}

int Delete_(pListNode s) 
{
	if (!s)
	{
		perror("Delete_L_NULL");
		return 0;
	}
	s->prev->next = s->next;
	s->next->prev = s->prev;
	free(s);
	s = NULL;
	return 1;
}

//双链表头删
void Delete_Head(pListNode L)
{
	assert(L);//判断当前指针是否为空指针
	assert(!Empty_List(L));  //判断链表是否为空
	Delete_(L->next);
}

//双链表尾删
void Delete_End(pListNode L)
{
	assert(L);
	assert(!Empty_List(L));
	Delete_(L->prev);  //删除某一位置的元素
}


void Printf_List(pListNode L)   
{
	if (!L)
	{
		perror("Printf_L_NULL");
		return;
	}
	pListNode s = L->next;
	cout << "表中元素为:";
	while (s != L)
	{
		cout << s->data << " ";
		s = s->next;
	}
	cout << endl << "打印完毕!" << endl;
}

//查找
pListNode Find_List(pListNode L, Datatype x)   //按值查找
{
	if (!L)
	{
		perror("Find_L_NULL");
		return NULL;
	}
	pListNode s = L->next;
	while (s != L)
	{
		if (s->data == x)
		{
			return s;
		}
		else
		{
			s = s->next;
		}
	}
	if (s = L)
	{
		cout << "Can't find " << x << " in List!" << endl;
	}
	return NULL;
}
//销毁双链表
int Destory_List(pListNode L)  
{
	if (!L)
	{
		perror("Destory_L_NULL");
		return 0;
	}
	pListNode s = L->next, r = s;
	while (s != L)
	{
		s = s->next;
		free(r);
		r = s;
	}
	free(L);
	L = NULL;
	return 1;
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ryan.Alaskan Malamute

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值