双向带头循环链表——C语言实现

目录

一、概念

二、结构

 三、双向循环链表的接口

1、创建节点

2、初始化

3、打印链表

4、链表的尾插

5、链表的尾删

6、链表的头插

7、链表的头删

四、优缺点


一、概念

        双向带头循环链表是具有哨兵位头节点的链表,并且它的每个结点中都有两个指针,分别指向直接后继和直接前驱。

二、结构

typedef int DoubleListDataType;
//带头双向循环链表
typedef struct DoubleListNode
{
	struct DoubleListNode* previous;	//指向直接前趋的指针域
	DoubleListDataType data;	//数据域
	struct DoubleListNode* next;	//指向直接后继的指针域
}DoubleListNode;

 三、双向循环链表的接口

1、创建节点

//创建节点
DoubleListNode* BuyDoubleListNode(DoubleListDataType x)
{
	//动态开辟节点空间
	DoubleListNode* newnode = (DoubleListNode*)malloc(sizeof(DoubleListNode));
	//判断动态开辟空间是否成功
	assert(newnode);
	//节点的初始化
	newnode->previous = NULL;
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

2、初始化

//初始化--头结点的创建
DoubleListNode* DoubleListInit()
{
	//创建头节点
	DoubleListNode* phead = BuyDoubleListNode(-1);
	//头节点初始化
	phead->previous = phead;
	phead->next = phead;
	return phead;
}

3、打印链表

//打印链表
void DoubleListPrint(DoubleListNode* phead)
{
	//链表不为空
	assert(phead);
	DoubleListNode* current = phead->next;	//当前节点
	while (current!=phead)
	{
		printf("%d ", current->data);
		current = current->next;
	}
	printf("\n");

}

4、链表的尾插

//尾插
void DoubleListPushBack(DoubleListNode* phead, DoubleListDataType x)
{
	//链表不为空
	assert(phead);
	//创建新节点
	doublelistnode* newnode = buydoublelistnode(x);
	//原链表的尾节点
	doublelistnode* endnode = phead->previous;
	//修改新节点的指向前趋的指针域(指向原尾节点)
	newnode->previous = endnode;
	//修改新节点的指向后继的指针域(指向头节点)
	newnode->next = phead;
	//修改原尾节点的指向后继的指针域(指向新节点)
	endnode->next = newnode;
	//修改头节点的指向前趋的指针域(指向新节点)
	phead->previous = newnode;
}

5、链表的尾删

//尾删
void DoubleListPopBack(DoubleListNode* phead)
{
	//链表不为空
	assert(phead);
	//链表不能只包含头节点
	assert(phead->next != phead);

	//尾节点
	DoubleListNode* EndNode = phead->previous;
	//倒数第二个节点
	DoubleListNode* PrevEndNode = EndNode->previous;
	//修改倒数第二个节点的指向后继的指针域(指向头节点)
	PrevEndNode->next = phead;
	//修改头节点的指向前趋的指针域(指向新的尾节点)
	phead->previous = PrevEndNode;

	//释放原来的尾节点
	free(EndNode);
	EndNode = NULL;

}

6、链表的头插

//头插
void DoubleListPushFront(DoubleListNode* phead, DoubleListDataType x)
{
	//链表不为空
	assert(phead);

	//创建新节点
	DoubleListNode* NewNode = BuyDoubleListNode(x);
	//第二个节点(头节点的下一个节点)
	DoubleListNode* SecondNode = phead->next;
	//修改新节点的两个指针域
	NewNode->previous = phead;
	NewNode->next = SecondNode;
	//修改第二个节点的指向前趋的指针域
	SecondNode->previous = NewNode;
	//修改头节点的指向后继的指针域
	phead->next = NewNode;
}

7、链表的头删

//头删
void DoubleListPopFront(DoubleListNode* phead)
{
	//链表不为空
	assert(phead);
	//链表不能只包含头节点
	assert(phead->next != phead);

	//第二个节点(头节点的下一个节点)
	DoubleListNode* SecondNode = phead->next;
	//第三个节点(头节点之后的第二个节点)
	DoubleListNode* ThirdNode = SecondNode->next;
	//修改头节点与第三个节点的指针域
	ThirdNode->previous = phead;
	phead->next = ThirdNode;
	//释放第二个节点
	free(SecondNode);

}

8、在 pos 节点之前插入新节点

//在 pos 节点之前插入
void DoubleListInsert(DoubleListNode* pos, DoubleListDataType x)
{
	//创建新节点
	DoubleListNode* NewNode = BuyDoubleListNode(x);
	//pos节点之前的节点
	DoubleListNode* PrevPosNode = pos->previous;
	//修改指针域
	NewNode->previous = PrevPosNode;
	NewNode->next = pos;
	PrevPosNode->next = NewNode;
	pos->previous = NewNode;
}

9、删除 pos 节点

//删除 pos 节点
void DoubleListErase(DoubleListNode* pos)
{
	assert(pos);
	//pos节点之前的节点
	DoubleListNode* PrevPosNode = pos->previous;
	//pos节点之后的节点
	DoubleListNode* NextPosNode = pos->next;
	//修改指针域
	PrevPosNode->next = NextPosNode;
	NextPosNode->previous = PrevPosNode;
	//释放pos
	free(pos);
	pos = NULL;//这一句实际无意义,因为参数类型为 DoubleListNode* ,而不是DoubleListNode** 
}

10、销毁链表

//销毁
void DoubleListErase(DoubleListNode** pos)
{
	assert(pos);
	assert(*pos);
	DoubleListNode* cur = (*pos)->next;
	while (cur != (*pos))
	{
		DoubleListNode* next = cur->next;
		DoubleListErase(cur);
		cur = next;
	}
	free(*pos);
	*pos = NULL;
}

四、优缺点

优点:1、在任意位置插入删除效率高,时间复杂度为O(1)。2、按需申请空间
缺点:不支持随机访问,一些快排,二分查找在这种结构上不适用

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值