双向带头循环链表的基础结构
双向带头循环链表(Doubly Circular Linked List)是一种数据结构,它与普通链表类似,但是每个节点除了有指向下一个节点的指针外,还有指向前一个节点的指针,而且它的头节点指向尾节点,尾节点也指向头节点,形成了一个循环。因此,我们能得到它的基础结构为
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
双向带头循环链表的数据操作
①创建返回链表的头结点
//创建一个新节点
ListNode* BuyListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
printf("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
②创建一个新节点
// 创建返回链表的头结点.
ListNode* ListCreate()
{
ListNode* head = BuyListNode(-1);
head->next = head;
head->prev = head->next;
return head;
}
③双向链表销毁
// 双向链表销毁
void ListDestory(ListNode* pHead)
{
ListNode* tail = pHead->next;
while (tail != pHead)
{
ListNode* tmp = tail->next;
free(tail);
tail = tmp;
}
free(tail);
tail = NULL;
}
④双向链表打印
// 双向链表打印
void ListPrint(ListNode* pHead)
{
ListNode* cur = pHead->next;
printf("<-pHead->");
while (cur != pHead)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("\n");
}
⑤双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
ListNode* newnode = BuyListNode(x);
ListNode* tail = pHead->prev;
tail->next = newnode;
newnode->prev = tail;
newnode->next = pHead;
pHead->prev = newnode;
}
⑥判断双向链表是否为空
//判断双向链表是否为空
bool LTEmpty(ListNode* phead)
{
return phead->next != phead;
}
⑦双向链表尾删
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
assert(pHead);
assert(LTEmpty(pHead));
ListNode* tail = pHead->prev;
tail->prev->next = pHead;
pHead->prev = tail->prev;
free(tail);
tail = NULL;
}
⑧双向链表头插
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
ListNode* newnode = BuyListNode(x);
pHead->next->prev = newnode;
newnode->next = pHead->next;
newnode->prev = pHead;
pHead->next = newnode;
}
⑨双向链表头删
// 双向链表头删
void ListPopFront(ListNode* pHead)
{
assert(pHead);
assert(LTEmpty(pHead));
ListNode* tmp = pHead->next;
pHead->next->next->prev = pHead;
pHead->next = pHead->next->next;
free(tmp);
tmp = NULL;
}
⑩双向链表查找
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
ListNode* pos = pHead->next;
while (pos != pHead)
{
if (pos->data == x)
{
return pos;
}
pos = pos->next;
}
return NULL;
}
⑪双向链表在pos的前面进行插入
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
ListNode* prev = pos->prev;
ListNode* newnode = BuyListNode(x);
newnode->next = pos;
newnode->prev = prev;
prev->next = newnode;
pos->prev = newnode;
}
⑫双向链表删除pos位置的节点
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
ListNode* tmp = pos;
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
}
双向带头循环链表的优势与劣势
①优势
双向带头循环链表能够高效地实现数据的前后遍历,可以在链表中任何位置快速地插入或删除节点,而不需要遍历整个链表。
它允许双向遍历,这使得某些算法和数据操作(例如,快速删除和反转链表)更加容易和高效。
对于需要使用首尾相接的数据结构的场景(例如,实现循环队列),双向带头循环链表是一种自然的选择。
②劣势
它需要更多的存储空间来存储前驱指针,这可能会占用额外的内存空间。
由于需要同时维护前驱和后继指针,操作的实现可能会更加复杂,代码的可读性和可维护性也可能会受到影响。
在某些场景下,双向带头循环链表可能并不是最优的数据结构选择,例如,当数据具有固定的大小和顺序时,数组可能是更好的选择。