当前位置:   article > 正文

【数据结构初阶】图解链表(二)双向带头循环链表_带头的双链表示意图

带头的双链表示意图

单链表在之前的博客里已经详细讲解过了 (指路导航 无哨兵位单向非循环链表),接下来讲解的是 双向带头循环链表。

源码地址链接

注意:要转图请提前打招呼

一级指针还是二级指针?

首先我们要确定的问题是带哨兵位的链表,需要传啥类型的实参?
在这里插入图片描述
由图见,因为带哨兵位,不对phead进行修改,所以只需要传一级指针。

结点结构体

// 带头+双向+循环链表增删查改
typedef int LTDataType;

typedef struct ListNode {
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

函数接口

// 创建返回链表的头结点
LTNode* ListCreate();
// 双向链表销毁  
void ListDestroy(LTNode* phead);
// 双向链表打印
void ListPrint(LTNode* phead);
// 双向链表尾插
void ListPushBack(LTNode* phead, LTDataType x);
// 双向链表尾删
void ListPopBack(LTNode* phead);
// 双向链表头插
void ListPushFront(LTNode* phead, LTDataType x);
// 双向链表头删
void ListPopFront(LTNode* phead);
// 双向链表查找
LTNode* ListFind(LTNode* phead, LTDataType x);
// 双向链表在pos的前面进行插入 
void ListInsert(LTNode* pos, LTDataType x);
// 双向链表删除pos位置的结点 
void ListErase(LTNode* pos);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

创建返回链表的头结点

开辟一个哨兵位结点,结点next和prev指向自己。

LTNode* ListCreate()
{
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	phead->next = phead;
	phead->prev = phead;
	return phead;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

双向链表销毁

  1. 链表要求逐个销毁
  2. 因为这里传的是一级指针,free了哨兵位后会使他成为野指针,需要在调用函数处置NULL。
void ListDestroy(LTNode* phead)
{
	assert(phead); 
	// 逐个销毁
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

双向链表打印

逐个打印、注意结束条件即可。

void ListPrint(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

双向链表尾插

在这里插入图片描述
老套路、不废话。

void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;

	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

双向链表尾删

在这里插入图片描述

void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead); // 不能删除哨兵位

	LTNode* oldtail = phead->prev;
	LTNode* newtail = oldtail->prev;

	phead->prev = newtail;
	newtail->next = phead;

	free(oldtail);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

双向链表头插

在这里插入图片描述

void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* first = phead->next;
	LTNode* newnode = BuyListNode(x);

	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

双向链表头删

在这里插入图片描述

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	LTNode* first = phead->next, * second = first->next;
	//phead  first  second
	phead->next = second;
	second->prev = phead;
	free(first);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

双向链表查找

遍历思想。

LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
			return cur;

		cur = cur->next;
	}
	return NULL;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

双向链表在pos的前面进行插入

在这里插入图片描述

void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* prevnode = pos->prev;
	LTNode* newnode = BuyListNode(x);

	// prevnode  newnode pos
	prevnode->next = newnode;
	newnode->prev = prevnode;
	newnode->next = pos;
	pos->prev = newnode;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

双向链表删除pos位置的结点

在这里插入图片描述

void ListErase(LTNode* pos)
{
	assert(pos);

	LTNode* next = pos->next, * prev = pos->prev;
	// prev pos next
	prev->next = next;
	next->prev = pos;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

总结

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

码文不易,欢迎三连,笔芯感谢!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/725119
推荐阅读
相关标签
  

闽ICP备14008679号