标题:双向循环链表
一、带头结点的双向循环链表
双向循环链表(带尾指针)核心关系梳理
1. 尾节点的直接后继。尾节点 tail 的 next 直接指向头结点 head,构成循环。 tail \\rightarrow next = head
2. 头节点的直接前驱。头结点 head 的 prev 直接指向尾节点 tail,不是空指针。 head \\rightarrow prev = tail
3. 完整闭环关系 – 尾后继: tail->next = head – 头前驱: head->prev = tail – 任意节点 p: p->next 后继, p->prev 前驱 – 整个链表首尾互相指向,不存在 NULL ,这是循环链表特征。
4. 带尾指针结构补充: 若链表用尾指针 tail 标识(不单独存头指针),那么头结点 = tail->next ,访问头节点无需额外变量,头节点前驱永远是 tail 。
二、双向循环链表:带头节点 VS 不带头结点
带头节点链表 vs 不带头节点链表(单链表举例) 2.1. 定义 (1)不带头节点链表 链表第一个节点直接存有效数据,头指针 head 直接指向第一个数据节点。 结构: head → 节点1(数据) → 节点2(数据) → … → NULL – 空链表: head = NULL
(2)带头节点链表 链表最前面多一个无有效数据的空节点(头结点),头指针永远指向这个头结点,真正数据节点挂在头结点后面。 结构: head → 头结点(无数据) → 节点1(数据) → 节点2(数据) → … → NULL – 空链表: head->next = NULL , head 本身不为空 2.2. 关键区别 ① 判空逻辑
– 不带头: if (head == NULL) – 带头: if (head->next == NULL)
② 头部插入/删除(最大优势)
不带头 在链表最前面新增节点: 要修改头指针 head ,代码需要单独处理头指针,逻辑分支多。 删除第一个节点:也要更新 head = head->next 。 带头 头结点固定不动,所有增删操作逻辑统一,不用特殊处理头部。 不管在表头、表中、表尾插入,代码写法完全一致,简化逻辑。
③ 初始化
– 不带头: LinkList head = NULL; – 带头:先申请头结点内存 head = malloc(sizeof(Node)); head->next = NULL;
C语言单链表
不带头节点初始化 c typedef struct Node{ int data; struct Node *next; }Node,*LinkList; LinkList head = NULL; // 空链表直接置空 带头节点初始化 c LinkList head; head = (Node*)malloc(sizeof(Node)); head->next = NULL; // 头结点后继为空
三、区分普通双向链表 vs 双向循环链表
普通双向链表(不循环): – 头结点 prev = NULL – 尾结点 next = NULL 双向循环链表:所有指针都不为空,首尾互指。



