【码道初阶】Leetcode234进阶版回文链表:牛客一道链表Hard,链表的回文结构——如何用 O(1) 空间“折叠”链表? - 详解

【LeetCode/牛客】链表的回文结构:如何用 O(1) 空间“折叠”链表?

在判断字符串或数组是否回文时,我们通常使用双指针从两头向中间逼近。但对于单向链表,我们无法轻易地“从后往前”遍历。

最直观的方法是将链表元素存入数组或栈中,但这就需要 O(N)O(N)O(N) 的额外空间。如果题目卡死了 空间复杂度 O(1),我们该怎么办?

今天我们要拆解的,就是一种通过修改链表结构来实现回文判断的高阶解法。

1. 核心解题思路:找到中点,折叠比较

既然无法从后往前遍历,那我们不妨把链表的后半段反转过来,让它指向中间。这样,我们就可以分别从“头”和“尾”出发,向中间走,一一比对。

整个过程分为三步(这也是代码中的三个主要模块):

  1. 快慢指针找中点:确定链表的一半在哪里。
  2. 反转后半部分:将后半段链表的指针方向反转,使其指向中点。
  3. 双指针汇合判断:一个从头走,一个从尾走,检查数值是否相等。

2. 代码深度解析

让我们结合代码,一步步看懂这个过程。

第一步:快慢指针找中点

// 1、快慢双指针遍历找到中间元素
ListNode fast = A;
ListNode slow = A;
while(fast != null && fast.next != null)
{
fast = fast.next.next;
slow = slow.next;
}
  • 原理fast 一次走两步,slow 一次走一步。当 fast 走到链表末尾时,slow 刚好走到链表的中点。
  • 细节
    • 如果是奇数个节点(如 1->2->3->2->1),slow 会停在正中间(3)。
    • 如果是偶数个节点(如 1->2->2->1),slow 会停在前半段的最后一个(第一个 2)。

第二步:反转后半部分(高能预警)

这是代码中最精妙也最容易晕的地方:

// 2、反转链表
ListNode cur = slow.next;
while(cur != null)
{
ListNode curN = cur.next;
cur.next = slow; // 【关键】反转指向,指向前一个节点
slow = cur;      // slow 移动到当前节点,作为新的“头”
cur = curN;      // cur 继续处理下一个
}

此时链表变成了什么样?
假设链表是 1 -> 2 -> 3 -> 2 -> 1
经过这一步,原本向右指的箭头,在后半段变成了向左指:
结构变成了:1 -> 2 -> 3 <- 2 <- 1

  • A 依然指向头部 1
  • 注意:此时变量 slow 已经跑到了链表的最末尾(也就是反转后的“新头”),即右边的 1

第三步:双向逼近判断

// 3、判断回文
while(A != slow)
{
// 值不相等,直接判定不是回文
if(A.val != slow.val)
{
return false;
}
// 【易错点高亮】偶数节点的特殊处理
if(A.next == slow)
{
return true;
}
slow = slow.next; // 从后往前走(因为指针反转了)
A = A.next;       // 从前往后走
}
return true;

3. ⚠️ 易错点与逻辑陷阱(重点)

这段代码虽然短,但有两个非常容易写错的逻辑,必须重点标注!

易错点 1:反转逻辑中的节点连接

很多人在反转链表时习惯把 slow.next 置空,或者切断中间的联系。但在这个解法中,我们保留了中间的连接

  • cur.next = slow:这一句把后半段的节点反向指回了 slow
  • 对于奇数链表,中间节点(如 3)成为了连接点,左边指向它,右边也指向它。

易错点 2:偶数链表的判断陷阱 (A.next == slow)

这是整段代码最容易被忽略的细节!

假设链表是偶数长度:1 -> 2 -> 2 -> 1

  1. 反转后结构变成:1 -> 2(前) <-> 2(后) <- 1
  2. A 在左边 1slow 在右边 1。值相等,继续走。
  3. A 走到 2(前)slow 走到 2(后)
  4. 此时注意Aslow 指向的是两个不同的节点对象,虽然它们的值都是 2,但内存地址不同,所以 while(A != slow) 循环继续。
  5. 在下一轮循环前,如果没有 if(A.next == slow) 这个判断:
    • A 会继续往后走到 2(后)
    • slow 会继续往前走到 2(前)
    • 两人“擦肩而过”,导致循环多跑或者逻辑混乱。

必须提前判断:
A 的下一个节点就是 slow 时(即 Aslow 相邻),说明对于偶数链表来说,已经比对完毕了,直接返回 true

4. 总结

这道题考核了对链表指针的绝对掌控力。

  • 常规解法:栈/数组,空间 O(N)。
  • 大神解法:快慢指针找中点 + 局部反转,空间 O(1)。

代码通过原地修改链表指向,巧妙地把一个单向链表变成了“两头往中间走”的结构,尤其是对偶数链表 A.next == slow 的判断,是点睛之笔。

虽然这种做法破坏了原链表的结构(如果需要还原,还需要再反转一次),但在算法题中,这是追求极致空间效率的标准答案。


这个算法在面试中非常高频,理解了它,链表类的题目基本就通了一半。

posted @ 2026-01-24 15:52  clnchanpin  阅读(3)  评论(0)    收藏  举报