LeetCode 面试题 02.06. 回文链表
题目描述

题意分析
判断链表节点值从前往后和从后往前是否相同。进阶要求 $O(n)$ 时间、$O(1)$ 额外空间,因此不把所有值复制到数组,而是通过改动指针让后半段能够反向读取;空链表和单节点链表都返回
true。
解法:找中点、反转后半段并比较
核心思路
[!blue]
回文要求首尾成对相等,但单链表不能直接从尾部往前走。先找到前半段末尾,再反转后半段,就能让两个向前移动的指针依次访问原链表中互相对称的节点。
初始化
slow = head、fast = head.next,每轮慢指针走一步、快指针走两步。循环结束时,偶数长度的slow位于左半段末尾;奇数长度时位于中间节点。这样slow.next后面恰好有floor(n / 2)个需要参与比较的节点,中间节点无需比较。先保存
slow.next再断开两段。反转后半段时,每次先保存当前节点原来的后继,再把当前节点插到哑节点之后,最后沿保存的后继继续处理。哑节点后的链表始终是“已处理部分的逆序”,全部处理后就得到完整的反向后半段。从原头和反转后的头同时前进,每次比较节点值。一旦不同就不是回文;如果较短的后半段全部匹配,所有对称位置都已经检查,返回
true。比较循环由后半段控制,既适用于偶数长度,也会自然跳过奇数长度留下的中间节点。
解题步骤
- 空链表直接返回 true。
- 令 fast=head.next,slow=head,快指针每次两步、慢指针一步。
- 先保存 slow.next,再断开两半;头插法反转后半段,改指针前保存后继。
- 从两半的头并行比较值,出现不同返回 false;短链走完返回 true。
代码实现
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null) {
return true;
}
ListNode slow = head;
ListNode fast = head.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode p = slow.next;
slow.next = null;
ListNode dummy = new ListNode(0);
while (p != null) {
ListNode next = p.next;
p.next = dummy.next;
dummy.next = p;
p = next;
}
p = dummy.next;
while (p != null) {
if (head.val != p.val) {
return false;
}
head = head.next;
p = p.next;
}
return true;
}
}
func isPalindrome(head *ListNode) bool {
if head == nil {
return true
}
slow, fast := head, head.Next
for fast != nil && fast.Next != nil {
slow, fast = slow.Next, fast.Next.Next
}
p := slow.Next
slow.Next = nil
dummy := &ListNode{}
for p != nil {
next := p.Next
p.Next = dummy.Next
dummy.Next = p
p = next
}
p = dummy.Next
for p != nil {
if head.Val != p.Val {
return false
}
head = head.Next
p = p.Next
}
return true
}
复杂度分析
- 时间复杂度:$O(n)$,寻找切点、反转后半段和比较都只需线性遍历。
- 空间复杂度:$O(1)$,只使用固定数量的指针和一个哑节点,不保存整条链表的值。
关键点总结
[!green]
以反转后的短链控制比较次数。当前实现通过原地断链、重连实现常数额外空间,调用后原链表结构会改变。
易错点总结
[!yellow]
- 快慢指针起点要和切点配套;当前写法若改 fast=head,偶数长度会错过一对比较。
- 比较节点值而不是对象引用。
- 反转时不保存原后继会丢失剩余节点。
- 仅凭求和或异或无法验证对称位置相等,必须逐对比较节点值。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 206. 反转链表 | 简单 | 直接复用原地反转的存后继、改指针、推进步骤,本题只反转后半段。 |
| 876. 链表的中间结点 | 简单 | 同样用快慢指针定位中点,本题需要前半段末尾,偶数长度的取整约定不同。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!