← 返回题目列表

如何判断一个单链表是否为回文链表?

高频 中等 第 10 / 28 题 更新于 2026/07/29
链表快慢指针回文

简化版

最优解 O(1) 空间三步走:快慢指针找中点 → 反转后半段 → 从两头向中间逐个比对。前半段从头走、后半段从反转后的尾走,值一路相等就是回文。比完可以再把后半段反转回去还原链表。时间 O(n)、空间 O(1)。

详细版

单链表不能像数组那样从两端往中间夹(没有 prev 往回走),所以要想 O(1) 空间判回文,得先把「后半段反转」制造出一个能从尾往前走的链。

boolean isPalindrome(ListNode head) {
    if (head == null || head.next == null) return true;
    // 1. 快慢指针找中点(slow 停在前半段末尾/中间)
    ListNode slow = head, fast = head;
    while (fast.next != null && fast.next.next != null) {
        slow = slow.next; fast = fast.next.next;
    }
    // 2. 反转后半段(slow.next 起)
    ListNode second = reverse(slow.next);
    // 3. 双指针比对
    ListNode p1 = head, p2 = second;
    boolean ok = true;
    while (p2 != null) {
        if (p1.val != p2.val) { ok = false; break; }
        p1 = p1.next; p2 = p2.next;
    }
    slow.next = reverse(second);   // 可选:还原链表
    return ok;
}
ListNode reverse(ListNode h) {
    ListNode prev = null;
    while (h != null) { ListNode nx = h.next; h.next = prev; prev = h; h = nx; }
    return prev;
}

完整版教学

一、为什么不能直接双指针夹

数组判回文很简单:左右两个指针往中间靠,比 a[l]a[r]。但单链表只能从头往后走,没有 prev 指针,右指针没法往左移动。所以必须想办法「制造出一个能反向走的结构」——要么额外存值(O(n) 空间),要么原地反转后半段(O(1) 空间)。

二、朴素解法:借助额外空间

  • 拷进数组/列表:遍历链表把值全放进 ArrayList,再用数组的双指针夹着比。O(n) 时间、O(n) 空间,最好写。
  • 用栈:把后半段(或全部)压栈,再一边出栈一边和前半段比。栈的 LIFO 天然实现「反向」。

面试能先说这个证明思路清晰,再给出 O(1) 空间的进阶解。

三、O(1) 空间三步法详解

  1. 找中点:快指针一次走两步、慢指针一步,快指针到尾时慢指针在中间。用 fast.nextfast.next.next 做条件能让 slow 停在「前半段的最后一个节点」,方便把 slow.next 之后当后半段。
  2. 反转后半段:标准的链表反转,把后半段变成从原尾节点往回的链。
  3. 对比:一个指针从 head(前半段头)、一个从反转后的后半段头,同步向中间推进,逐个比值。后半段较短或等长,以 p2 != null 为界比完即可(奇数长度时正中间那个不用比)。

四、奇偶长度的处理

  • 偶数个节点(如 1→2→2→1):前后两半等长,比对到 p2 走完即可。
  • 奇数个节点(如 1→2→3→2→1):正中间的 3 是对称轴,不需要比较。用上面的快慢指针写法,中点会被划到前半段,后半段更短,while (p2 != null) 自然跳过中间节点,无需特判。

五、要不要还原链表

反转后半段破坏了原链表结构。如果调用方之后还要用这个链表,应当在比完后再反转一次后半段接回去(代码里 slow.next = reverse(second))。面试中主动提这一点是加分项——说明你考虑到了副作用。

解法时间额外空间是否修改链表
拷贝到数组后双指针O(n)O(n)不修改
栈保存后半段/全部节点值O(n)O(n)不修改
找中点 + 反转后半段O(n)O(1)会修改,最好还原

具体例子:1→2→3→2→1 中,快慢指针让 slow 停在 3,反转 slow.next 得到 1→2,然后用 head1→2 与反转后的 1→2 比对。中间的 3 是对称轴,不需要参与比较。

回文链表 O(1) 空间解法的代价是“临时改链”。如果面试官关心副作用,记得说会把后半段再反转接回去。

六、常见误区与追问

  • 误区:单链表可以像数组一样左右夹逼。 单链表没有前驱指针,右侧不能往左走,必须借助额外空间或反转后半段。
  • 误区:奇数长度必须单独删除中间节点。 用前半段末尾作为 slow 时,后半段从 slow.next 开始,中间节点自然跳过。
  • 误区:比对时应该循环到两个指针都为空。 后半段长度小于或等于前半段,通常以 p2 != null 为准。
  • 追问:为什么要还原链表? 如果函数调用者还要继续使用原链表结构,不还原会留下被反转的后半段。
  • 追问:能否用递归判断回文? 可以借助递归栈模拟后序比较,但空间仍是 O(n),不如 O(1) 的反转后半段。
  • 追问:空链表和单节点是否是回文? 通常认为是回文,因为没有任何一对不相等的对称元素。

七、加强记忆

单链表判回文 O(1) 空间三步:快慢指针找中点 → 反转后半段 → 两头向中间逐个比值(因为单链表不能像数组那样反向夹,得先造出可反向走的后半段)。奇数长度时中间节点自动跳过。比完最好把后半段反转回去还原链表。图省事可拷进数组用双指针,O(n) 空间。