循环链表节点结构需使尾节点next指向头节点以形成闭环;C++中常用单向结构,初始化首节点时须令next=head;遍历时用do-while配合p!=head判据防死循环;插入删除操作均需维护闭环,尤其注意头节点变更时更新尾指针。

循环链表的节点结构怎么定义
关键在于让最后一个节点的 next 指针不为 nullptr,而是指向头节点(或首节点)。C++ 中最常用的是单向循环链表,节点结构和普通链表几乎一样,但语义上必须保证闭环:
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};初始化时不能留空指针,否则后续遍历时会崩溃。插入第一个节点后,必须立即将它的 next 指向自己:head->next = head;
如何安全地插入新节点(头插/尾插)
头插法最简单,但要注意:插入后所有节点仍需维持循环——新节点的 next 应指向原头节点,而原链表最后一个节点的 next 必须更新为新头节点。尾插法则需要遍历找尾,但别用 while (p->next != nullptr),那会无限循环;正确判据是 p->next != head(假设 head 非空)。
- 头插:先保存原
head,新节点next指向它,再遍历到尾并更新其next指向新节点,最后设head = newNode - 尾插:若链表为空,直接
newNode->next = newNode;否则从head出发,用while (p->next != head)找到最后一个节点,再接上 - 插入后务必检查是否破坏了循环:任意节点出发,走一圈应能回到起点,且不会遇到
nullptr
遍历循环链表时怎么避免死循环
常见错误是写成 while (p != nullptr) 或 while (p->next != nullptr) —— 这两种在循环链表里永远不成立,直接卡死。正确方式是记录起始节点,用「走完一圈就停」的逻辑:
立即学习“C++免费学习笔记(深入)”;
ListNode* p = head;
if (!p) return; // 空链表
do {
// 处理 p->val
p = p->next;
} while (p != head);注意:do-while 比 while 更稳妥,确保至少访问一次;如果用 while,得先判断 head 是否为空,再进循环,且条件写成 p->next != head 仅适用于访问除头外的节点,容易漏处理头节点。
删除节点时如何维护循环结构
删头节点最危险:不能只改 head = head->next 就完事,必须同步更新原尾节点的 next 指针。通用做法是先找到待删节点的前驱(即它的上一个节点),然后执行 prev->next = target->next。难点在于找前驱——不能靠 prev->next == target 就停,因为可能绕圈多次;必须加计数或用「当 prev->next != target 且 prev != target」双重约束,或者更干脆:遍历中记录前驱,一旦 curr == target 就跳出。
- 删唯一节点:把
head设为nullptr,并释放内存 - 删非头节点:找到前驱后断链,无需额外调整其他指针
- 删头节点:等价于删「前驱是尾节点」的节点,所以仍按通用流程找前驱,只是最终要重置
head
实际调试时,建议每次操作后手动验证 head->next 是否非空、任意节点出发走 N 步(N 为当前长度)是否回到原点——循环链表的 bug 往往在边界 case 上,比如 0 节点、1 节点、2 节点时指针连错。


















