带头双向链表是一种链表数据结构,相比于普通的双向链表,它包含一个额外的头结点。
带头双向链表的特点是:
- 头结点不存储实际数据,它仅用于标识链表的起点。
- 头结点的前驱指针(prev)指向链表的尾节点,头结点的后继指针(next)指向链表的第一个节点。
- 链表的最后一个节点的后继指针(next)指向头结点,链表的第一个节点的前驱指针(prev)也指向头结点,形成了循环。
带头双向链表的优点是:
- 头结点的存在简化了对链表的操作,可以方便地进行插入、删除等操作,而无需特殊处理链表为空的情况。
- 循环链表的结构可以更灵活地遍历链表中的所有节点,不需要额外的判断条件来结束遍历。
在实际应用中,带头双向链表常用于构建更复杂的数据结构,如双向循环队列、LRU Cache等。它也可以作为一种常见的链表实现方式,提供对链表的高效操作。
头文件
#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);
创建返回链表的头结点
ListNode* ListCreate()
{
ListNode* pHead = (ListNode*)malloc(sizeof(ListNode));
if (pHead == NULL)
{
perror("malloc fail");
exit(-1);
}
pHead->data = 0;
pHead->next = pHead;
pHead->prev = pHead;
return pHead;
}
双向链表销毁
void ListDestory(ListNode* pHead)
{
if (pHead == NULL)
return;
ListNode* cur = pHead->next;
while (cur != pHead)
{
ListNode* next = cur->next;
free(cur);
cur = next;
}
free(pHead);
}
双向链表打印
void ListPrint(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead;
while (cur->next != pHead )
{
cur = cur->next;
printf("%d-> ", cur->data);
}
printf("NULL\n");
return;
}
双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
if (pHead == NULL)
return;
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
if (newNode == NULL)
{
perror("malloc fail");
exit(-1);
}
newNode->data = x;
ListNode* cur = pHead->prev;
newNode->next = pHead;
newNode->prev = cur;
pHead->prev = newNode;
cur->next = newNode;
}
双向链表尾删
void ListPopBack(ListNode* pHead)
{
if (pHead == NULL || pHead->next == pHead)
return;
ListNode* cur = pHead->prev;
ListNode* prev = cur->prev;
prev->next = pHead;
pHead->prev = prev;
free(cur);
}
双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
if (pHead == NULL)
return;
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
if (newNode == NULL)
{
perror("malloc fail");
exit(-1);
}
newNode->data = x;
ListNode* first = pHead->next;
newNode->next = first;
newNode->prev = pHead;
pHead->next = newNode;
first->prev = newNode;
}
双向链表头删
void ListPopFront(ListNode* pHead)
{
if (pHead == NULL || pHead->next == pHead)
return;
ListNode* first = pHead->next;
ListNode* second = first->next;
pHead -> next = second;
second->prev = pHead;
free(first);
}
双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
if (pHead == NULL)
return NULL;
ListNode* cur = pHead->next;
while (cur != pHead)
{
if (cur->data = x)
return cur;
cur = cur->next;
}
return NULL;
}
双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
if (pos == NULL)
return;
ListNode* prev = pos->prev;
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
if (newNode == NULL)
{
perror("malloc fail");
exit(-1);
}
newNode->data = x;
prev->next = newNode;
newNode->prev = prev;
newNode->next = pos;
pos->prev = newNode;
}
双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
if (pos == NULL || pos->next == pos)
return;
ListNode* prev = pos->prev;
ListNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
}
测试
int main()
{
ListNode* pHead = ListCreate();
ListPushBack(pHead, 1);
ListPushBack(pHead, 2);
ListPushBack(pHead, 3);
ListPrint(pHead);
ListPopBack(pHead);
ListPrint(pHead);
ListPushFront(pHead, 4);
ListPushFront(pHead, 5);
ListPrint(pHead);
ListPopFront(pHead);
ListPrint(pHead);
ListNode* foundNode = ListFind(pHead, 4);
if (foundNode)
printf("Found: %d\n", foundNode->data);
ListInsert(foundNode, 5);
ListPrint(pHead);
ListErase(foundNode);
ListPrint(pHead);
ListDestory(pHead);
return 0;
}