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

Posted 流浪的蚂蚁森林

tags:

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


双向链表的概念

在前面学习链表的时候,不知道大家有没有过感受,链表是通过next链接起来的,这种链接是头去链接尾,但是在尾部就不是那么好往前找到头。

比如这么一个结构,如果说现在让输出1,2,3,4,5是不是很容易,直接顺序遍历,然后打印data域即可,但是如果需要反着打印的时候大家是不是就难受了,心想:如果5到4有链接,4到3有链接,3到2有链接,2到一有链接,如果是这样的结构,就打印十分方便了。
上面心中所想的结构其实就是双向链表了。
如下:
至于双向带头循环的链表:就是将头尾相连的双向链表了,这种链表结构就非常强大了,我们可以从任一点出发找到别的点。

算了来张百度百科的图片

现在来创建一个双向链表的结构:
其实就是多了一个prev指针的链表结构。

typedef struct ListNode

	struct ListNode* next;
	struct ListNode* prev;
	LDataType data;
ListNode;

双向链表的相关函数

初始化创建

注意创建就是指需要有一个头节点,对于双向链表来说,起码得有一个节点的,不然的话那算?算空气。这个节点首先就是自己链接自己的。

ListNode* ListCreate()

	ListNode* phead = BuyListNode(0);
	phead->next = phead;
	phead->prev = phead;
	return phead;

新建一个节点

ListNode* BuyListNode(LDataType x)

	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->next = NULL;
	node->prev = NULL;
	node->data = x;
	return node;

头插

头插的两种方式,一种是复用后面的在pos位置插入(对于头插来说pos就是phead->next,注意这里不是phead,phead->next才是指头插),另一种直接实现

复用

void ListPushFront(ListNode* phead, LDataType x)

	assert(phead);
	ListInsert(phead->next, x);

不复用

void ListPushFront(ListNode* phead, LDataType x)

	assert(phead);
	assert(phead->next != phead);//只有一个头节点的时候不要删除了
	ListNode* tail = phead->prev;
	ListNode* tailPrev = tail->prev;
	free(tail);
	tailPrev->next = phead;
	phead->prev = tailPrev;

尾插

尾插也是两种实现,依旧是使用pos插入,此时pos是phead,头是连着尾的

复用

void ListPushBack(ListNode* phead, LDataType x)

	assert(phead);
	ListInsert(phead, x);

不复用

void ListPushBack(ListNode* phead, LDataType x)

	assert(phead);
	ListNode* tail = phead->prev;
	ListNode* newnode = BuyListNode(x);
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;

头删

头删的时候注意要讨论不要删除最后的一个节点

复用


void ListPopFront(ListNode* phead)

	ListErase(phead->next);//复用头删

不复用

void ListPopFront(ListNode* phead)

	assert(phead);
	assert(phead->next != phead);
	ListNode* first = phead->next;
	ListNode* firstNext = first->next;
	phead->next = firstNext;
	firstNext->prev = phead;
	free(first);

尾删

尾删同理,至少保留一个节点~~

void ListPopBack(ListNode* phead)

	ListErase(phead->prev);

void ListPopBack(ListNode* phead)

	assert(phead);
	assert(phead->next != phead);
	ListNode* tail = phead->prev;
	ListNode* tailPrev = tail->prev;
	free(tail);
	tailPrev->next = phead;
	phead->prev = tailPrev;

打印节点

void ListPrint(ListNode* phead)

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

查找

ListNode* ListFind(ListNode* phead, LDataType x)

	assert(phead);
	ListNode* cur = phead->next;//指向头节点的next才是真实的值域
	while (cur != phead)
	
		if (cur->data == x)
		
			return cur;
		
		cur = cur->next;
	
	return NULL;

pos处插入节点

前面使用的头插尾插就是复用这个地方,对于为什么头插是phead->next,尾插是phead

void  ListInsert(ListNode* pos, LDataType x)

	assert(pos);
	ListNode* prev = pos->prev;
	ListNode* newNode = BuyListNode(x);
	prev->next = newNode;
	newNode->prev = prev;
	pos->prev = newNode;
	newNode->next = pos;

pos处删除节点

void  ListErase(ListNode* pos)

	assert(pos);
	ListNode* prev = pos->prev;
	ListNode* next = pos->next;
	free(pos);
	prev->next = next;
	next->prev = prev;

判断是否为空

int ListEmpty(ListNode* phead)

	return phead->next == phead ? 1 : 0;

判断大小

int ListSize(ListNode* phead)

	assert(phead);
	int len = 0;
	ListNode* cur = phead->next;//指向头节点的next才是真实的值域
	while (cur != phead)
	
		len++;
		cur = cur->next;
	
	return len;

销毁操作

void ListDestory(ListNode* phead)

	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	
	free(phead);

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

数据结构学习笔记二线性表之链表篇(双向链表)

带头双向循环链表的实现@线性表

初阶数据结构——线性表——链表——带头双向循环链表

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

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

数据结构:如何用C语言快速实现带头双向循环链表