数据结构:双向链表
一、双向链表概念与结构
双向链表概念
双向链表是一种链式存储的数据结构,每个节点包含两个指针:一个指向前驱节点(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;
}
2. 尾插与头插
尾插是指在链表的尾部插入新节点。对于带头节点的双向循环链表,尾插可直接通过头节点的 prev 指针定位尾节点,无需遍历链表,时间复杂度为 O(1)。
ListNode* LTCreate(type x) {
ListNode* ph = (ListNode*)malloc((ListNode));
(ph == ) {
perror();
();
}
ph->data = x;
ph->next = ph;
ph->prev = ph;
ph;
}
{
ListNode* p = LTCreate(x);
p->next = h;
p->prev = h->prev;
h->prev->next = p;
h->prev = p;
}


