数据结构初阶图解链表双向带头循环链表

Posted Bitdancing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构初阶图解链表双向带头循环链表相关的知识,希望对你有一定的参考价值。

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

源码地址链接

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

一级指针还是二级指针?

首先我们要确定的问题是带哨兵位的链表,需要传啥类型的实参?

由图见,因为带哨兵位,不对phead进行修改,所以只需要传一级指针。

结点结构体

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

typedef struct ListNode {
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

函数接口

// 创建返回链表的头结点
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);

创建返回链表的头结点

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

LTNode* ListCreate()
{
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

双向链表销毁

  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);
}

双向链表打印

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

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

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\\n");
}

双向链表尾插


老套路、不废话。

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;
}

双向链表尾删

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);
}

双向链表头插

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;
}

双向链表头删

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);
}

双向链表查找

遍历思想。

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;
}

双向链表在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;
}

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

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

	LTNode* next = pos->next, * prev = pos->prev;
	// prev pos next
	prev->next = next;
	next->prev = pos;
}

总结

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

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

以上是关于数据结构初阶图解链表双向带头循环链表的主要内容,如果未能解决你的问题,请参考以下文章

《链表》之带头双向循环链表

java实现单向循环链表

数据结构带头双向循环链表

数据结构——带头双向循环链表

数据结构学习笔记(单链表单循环链表带头双向循环链表)的增删查改排序等)

数据结构学习笔记(单链表单循环链表带头双向循环链表)的增删查改排序等)