双向链表概念与结构
带头双向循环链表
双向链表是一种链式存储的数据结构,每个节点包含两个指针:一个指向前驱节点(prev),一个指向后继节点(next),同时包含数据域(data)。这种结构允许双向遍历,支持更灵活的插入和删除操作,但相比单链表会增加一定的空间开销。
我们通常使用的是带头双向循环链表。它兼具'头节点''双向指针''循环结构'三大特性。其中的头节点不存储有效数据,仅作为哨兵位,核心价值是简化边界操作(如插入/删除首节点时无需特殊判断)。
双向链表结构
基于单向不带头不循环链表的知识,我们可以定义如下结构体:
typedef int type;
typedef struct ListNode {
type data; // 数据域
struct ListNode* prev; // 前驱指针
struct ListNode* next; // 后继指针
} ListNode;
实现双向链表
1. 初始化
在双向链表中,头节点需要初始化。数据域可以存任意值,前驱和后继指针都指向自己即可形成空循环。
void LTInit(ListNode** h) {
ListNode* ph = (ListNode*)malloc(sizeof(ListNode));
if (ph == NULL) {
perror("malloc fail!");
exit(1);
}
*h = ph;
(*h)->data = -1;
(*h)->next = *h;
(*h)->prev = *h;
}
这里使用二级指针 **h 是为了让函数内部能修改外部传入的头指针地址。初始化后,链表状态为 h <-> h。
2. 尾插与头插
尾插(LTPushBack)
对于带头节点的双向循环链表,尾插可直接通过头节点的 prev 指针定位尾节点,无需遍历,时间复杂度为 O(1)。
ListNode* LTcreat(type x) {
ListNode* ph = (ListNode*)((ListNode));
(ph == ) { perror(); (); }
ph->data = x;
ph->next = ph;
ph->prev = ph;
ph;
}
{
ListNode* p = LTcreat(x);
p->next = h;
p->prev = h->prev;
h->prev->next = p;
h->prev = p;
}


