Circular Linked List
循环链表
循环单链表
循环单链表将表尾结点的 next 指针指向头结点,而不是 NULL。
带头结点循环单链表的空表通常满足:
L->next == L
1 | bool InitCLinkList(LinkList *L) { |
特点:从任意结点出发,沿 next 指针可以回到起点并访问其他结点;从尾部到头部为 O(1)。
尾指针的价值
很多链表操作发生在头部或尾部。循环单链表中,若让 L 指向表尾结点,则:
- 找尾结点:
O(1)。 - 找头结点:
L->next,O(1)。 - 在表头或表尾插入更方便。
代价是插入、删除时要维护尾指针是否变化。
若 tail 指向尾结点,在表尾插入新结点 newNode:
1 | newNode->next = tail->next; // 新结点先指向头结点 |
这里 tail->next 是头结点。插入后必须更新 tail,否则尾指针仍指向旧尾。
循环双链表
循环双链表中:
- 表头结点的
prior指向表尾结点。 - 表尾结点的
next指向头结点。
带头结点循环双链表的空表通常满足:
1 | L->next = L; |
初始化:
1 | bool InitCDLinkList(DLinkList *L) { |
操作特点
循环双链表在插入、删除时通常不需要判断结点是否为表尾,因为表尾后继不是 NULL,而是头结点。这能减少边界特殊情况。
在 prev 后插入 newNode:
1 | newNode->next = prev->next; |
删除 prev 的后继 target:
1 | DNode *target = prev->next; |
循环双链表中这两段代码不需要判断 prev->next == NULL 或 target->next == NULL,因为不存在普通双链表意义上的空后继。真正需要防的是误删头结点,题目若要求保留头结点,应检查 target != head。
易错点
遍历循环链表时不能再用 p != NULL 作为终止条件,应以是否回到头结点或起始结点作为停止条件。
1 | for (LNode *p = L->next; p != L; p = p->next) { |
关联
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Elian's blog page!