各位观众老爷好呀,今天我们延续上篇文章:https://blog.csdn.net/zsgmy/article/details/136148950?spm=1001.2014.3001.5501(不带头单向不循环链表),我们本文将带大家完成带头双向循环链表。没有观看过上期的小伙伴,也可以点击链接观看一下哦。
首先,我们需要明确一下:带头即为在我们开辟链表时,先开辟了一块空间,这块空间中不带有数据,但是它指向我们链表中的第一位和最后一位。这个头我们后文称为哨兵位(head)。head的作用:我们的链表中绝对存在一个单位,不会是空的;我们在找头节点和尾节点会更容易,方便我们的头插尾插,头删尾删。
循环即我们的链表中的每一个单位带有两个指针,这两个指针一个指向前一位:prev 另一个指向下一位:next。链表的最后一位会指向head而不是NULL,所以我们后续找尾的条件也不在是next == NULL,而是next == head。
基于前文单链表的知识和上述条件,我们开始接下来的带头双向循环链表的编程。
首先,我们建立一个工程,这个工程组成如下:源文件:DSTList.c和Test.c 头文件:DSTList.h
DSTList.h:声明函数和应用库函数
DSTList.c:存放函数内容
Test.c:调试内容
接下来我们先完成结构体的定义:我们需要存放数据(data),一个指针指向前一个数据的地址(prev),一个指针指向下一个数据的地址(next),由此我们得到了我们的结构体:
我们需要先使用几个基础功能函数来实现我们的链表:创建链表( ListCreate),销毁链表(ListDestory),链表的尾插(ListPushBack),链表的打印( ListPrint)。
创建链表( ListCreate):我们需要一个哨兵位(head)所以我们需要先申请一块空间,设置我们的哨兵位,为此我们使用malloc动态开辟一个空间,然后我们将head 的 prev和 next指向head自身(循环)。这样我们就完成啦:
销毁链表(ListDestory):这里的删除我们从链表的尾部开始逐一释放空间。这里我们需要两个指针,一个指向下一个节点(释放空间),一个指向下下个节点(保存地址方便查询),达到一个在保存地址,一个在释放空间的效果:
我们使用一个while循环,直到只剩下哨兵位即可:
链表的尾插(ListPushBack):在我们当前的链表中,尾插因为双向的原因是非常容易的。我们只需要通过head->prev就能直接找到尾结点,然后申请一块新的空间插入即可:
链表的打印( ListPrint):我们在打印时直接通过while循环从head->next开始逐一打印,直到尾结点的next为head时停止即可:
通过完成上诉4个基本功能,我们的带头双向循环链表便完成了。
接下来我们再编写几个功能函数,丰富一下我们的链表就好啦。尾删(ListPopBack),头插(ListPushFront),头删(ListPopFront),查找(ListFind),在pos的前面进行插入(ListInsert),删除pos位置的节点(ListErase)。
尾删(ListPopBack):这一步我们直接通过head->prev找到尾结点,将尾结点的前一位与head链接,然后将尾结点释放即可:
头插(ListPushFront):我们直接将head->next给保存,然后将要头插的数据与head和head->next链接起来即可:
头删(ListPopFront):我们需要保存head->next和head->next->next,然后将head->next->next与head链接,然后把head->next空间释放即可:
查找(ListFind):我们需要将我们的链表进行遍历寻找,当找到相应目标时,返回他的地址即可:
在pos的前面进行插入(ListInsert):我们需要保存好pos->prev 的地址,然后将newnode和pos还有pos->prev链接即可:
删除pos位置的节点(ListErase):我们需要保存好pos->next和pos->prev的地址,然后将pos->next和pos->prev链接起来,然后将pos空间释放即可:
完成上述的功能之后我们就得到了一个功能相对完整的带头双向循环链表啦。
上文有任何可以改进和需要修改的欢迎各位大佬纠正!!!
本期代码如下:
//DSTList.c
#include"DSTList.h"
// 创建返回链表的头结点.
ListNode* ListCreate()
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc phead");
exit(-1);
}
newnode->_data = NULL;
newnode->_next = newnode;
newnode->_prev = newnode;
return newnode;
}
// 双向链表打印
void ListPrint(ListNode* pHead)
{
ListNode* tail = pHead->_next;
printf("哨兵位<=>");
while (tail->_next != pHead)
{
printf("%d<=>", tail->_data);
tail = tail->_next;
}
printf("%d\n", tail->_data);
}
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
ListNode* tail = pHead->_prev;
ListNode* newnode = ListCreate();
newnode->_data = x;
ListNode* head = pHead;
tail->_next = newnode;
newnode->_prev = tail;
head->_prev = newnode;
newnode->_next = head;
}
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
ListNode* tail = pHead->_prev;
ListNode* tailprev = pHead->_prev->_prev;
free(tail);
tailprev->_next = pHead;
pHead->_prev = tailprev;
}
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
ListNode* newnode = ListCreate();
newnode->_data = x;
ListNode* tail = pHead->_next;
pHead->_next = newnode;
newnode->_prev = pHead;
newnode->_next = tail;
tail->_prev = newnode;
}
// 双向链表头删
void ListPopFront(ListNode* pHead)
{
ListNode* tail = pHead->_next;
ListNode* tailnext = pHead->_next->_next;
ListNode* head = pHead;
free(tail);
head->_next = tailnext;
tailnext->_prev = head;
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
ListNode* tail = pHead->_next;
while (tail != pHead)
{
if (tail -> _data == x)
{
return tail;
}
tail = tail->_next;
}
return -1;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
ListNode* tail = pos->_prev;
ListNode* newnode = ListCreate();
newnode->_data = x;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = pos;
pos->_prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
ListNode* posprev = pos->_prev;
ListNode* posnext = pos->_next;
free(pos);
posprev->_next = posnext;
posnext->_prev = posprev;
}
// 双向链表销毁
void ListDestory(ListNode* pHead)
{
ListNode* tail = pHead->_prev;
ListNode* prev = pHead->_prev->_prev;
while (tail->_next != pHead)
{
free(tail);
tail = prev;
prev = prev->_prev;
}
free(tail);
}
//DSTList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
LTDataType _data;
struct ListNode* _next;
struct ListNode* _prev;
}ListNode;
// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);