拿来吧!C语言数据结构-带头双向循环链表

在这里插入图片描述
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了

链表初始化

首先创建一个结构体,给上前驱指针,后驱指针,还有数据域。

typedef int LTDateType;//数据类型
typedef struct ListNode
{
	LTDateType data;//数据域
	struct ListNode * next;//后驱指针
	struct ListNode * prev;//前去指针
}LTNode;

然后我们需要初始化函数来初始化链表,先创建一个头结点,让头结点的前驱指针和后驱指针都指向自己来实现循环。
在这里插入图片描述

LTNode* BuySListNode(LTDateType x)
{
	//开辟结点
	LTNode *node = (LTNode *)malloc(sizeof(LTNode));
	//判断是否成功开辟
	if (node == NULL)
	{
		printf("malloc fail\n");
		exit(-1);//退出程序
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	return node;
}
//初始化
LTNode* ListInit()
{
	LTNode *phead = BuySListNode(0);//头结点
	phead->next = phead;//后驱指向自己
	phead->prev = phead;//前驱指向自己
	return phead;
}

尾插

在这里插入图片描述

双向链表就比单向的很方便,单向链表就要先去找尾,这里就是head->prev.

void ListPushBack(LTNode* phead, LTDateType x)
{
	assert(phead);
	//记录前一个结点
	LTNode *tail = phead->prev;
	LTNode *newnode = BuySListNode(x);
	//建立链接关系
	tail->next = newnode;	
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

链表的打印

在这里插入图片描述

// 双向链表打印
void ListPrint(LTNode* phead)
{
	assert(phead);
	LTNode *cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

测试一下尾插

void test()
{
	LTNode *plist = NULL;
	plist = ListInit();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPrint(plist);
}

在这里插入图片描述

尾删

在这里插入图片描述
在尾删的时候,我们一直删一直删当链表没有结点还在删除就会出现问题。这是我们给个Empty的函数,配合assert一起使用就能解决这个问题。

bool ListEmpty(LTNode *phead)
{
	assert(phead);
	return phead->next == phead;
}
void ListPopBack(LTNode *phead)
{
	assert(phead);
	//没有结点就不能在删除了
	assert(!ListEmpty(phead));
	//先找到尾,在记录它前一个结点
	LTNode *tail = phead->prev;
	LTNode *tailprev = tail->prev;
	free(tail);
	tail = NULL;
	//在修改链接关系
	phead->prev = tailprev;
	tailprev->next = phead;
}
	ListPopBack(plist);
	ListPrint(plist);

在这里插入图片描述
当我们一直删,删完还在继续删就会报错。
在这里插入图片描述

头插

在这里插入图片描述

//头插
void ListPushFront(LTNode *phead, LTDateType x)
{
	assert(phead);
	
	LTNode *newnode = BuySListNode(x);
	//记录phead的后一个结点
	LTNode *next = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = next;
	next->prev = newnode;
}

在头插个10

	ListPushFront(plist, 10);
	ListPrint(plist);

在这里插入图片描述

头删

在这里插入图片描述

void ListPopFront(LTNode *phead)
{
	assert(phead);
	//没有结点就不能在删除了
	assert(!ListEmpty(phead));
	LTNode *tail = phead->next;
	phead->next = tail->next;
	tail->prev = phead;
	free(tail);
	tail = NULL;
}
	ListPopFront(plist);
	ListPrint(plist);

在这里插入图片描述

查找

查找,和打印链表差不多。

// 双向链表查找
LTNode* ListFind(LTNode* phead, LTDateType x)
{
	assert(phead);
	LTNode *cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

在pos之前插入结点

在这里插入图片描述

//在pos位置之前插入结点
void LTInsert(LTNode* pos, LTDateType x)
{
	assert(pos);
	//记录pos的前一个结点
	LTNode *prev = pos->prev;
	LTNode *newnode = BuySListNode(x);
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}
	LTNode * pos = ListFind(plist, 3);
	if (pos)
	{
		LTInsert(pos, 30);
	}
	ListPrint(plist);

在3之前插个30
在这里插入图片描述
那之前的头插和尾插就可以复用
在这里插入图片描述

删除pos位置的结点

void ListErase(LTNode* pos)
{
	assert(pos);
	//记录pos的前一个结点和后一个结点
	LTNode *prev = pos->prev;
	LTNode *next = pos->next;
	free(pos);
	pos = NULL;
	prev->next = next;
	next->prev = prev;
}
	LTNode * pos = ListFind(plist, 3);
	if (pos)
	{
		ListErase(pos);
	}
	ListPrint(plist);

在这里插入图片描述
3就被干掉了。
那同样头删和尾删也可以复用。
在这里插入图片描述

链表的销毁

最后一定要销毁。
一个一个结点的释放。

// 双向链表销毁
void ListDestory(LTNode* phead)
{
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = NULL;
		cur = next;
	}

	free(phead);
}

这些就是带头双向链表经常使用的,比单链表用起来很方表。但是面试考的多的是单链表,因为单链表的很麻烦。在这里插入图片描述

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_End丶断弦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值