LeetCode hot100:234 回文链表:快慢指针巧判回文链表
问题描述:
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
示例1:

输入:head = [1,2,2,1]
输出:true
示例2:

输入:head = [1,2]
输出:false
解决方法:
方法一:数组+双指针
算法思路:
-
遍历链表,将节点值复制到数组中;
-
使用双指针法判断数组是否为回文。
代码实现:
def isPalindrome(head):values = []curr = headwhile curr:values.append(curr.val)curr = curr.nextleft, right = 0, len(values) - 1while left < right:if values[left] != values[right]:return Falseleft += 1right -= 1return True
复杂度分析:
- 时间复杂度:O(n),需要遍历链表和数组各一次
- 空间复杂度:O(n),需要额外的数组空间存储节点值
方法二:快慢指针+翻转
算法思路:
-
使用快慢指针找到链表中点;
-
反转后半部分链表;
-
比较前半部分和反转后的后半部分。
代码实现:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def isPalindrome(self, head: Optional[ListNode]) -> bool:# 快慢指针if not head or not head.next:return Truefast = low = head# 查找反转的起点while fast and fast.next:low = low.nextfast = fast.next.next# 反转后半部分链表prev = Nonecur = lowwhile cur: # 迭代反转nxt = cur.nextcur.next = prevprev = curcur = nxt# 链表比较left = headright = prevwhile right:if left.val != right.val:return Falseleft = left.nextright = right.nextreturn True
复杂度分析:
- 时间复杂度:O(n),遍历链表的次数是常数倍
- 空间复杂度:O(1),只使用了几个指针变量
问题详解:

以链表 [ 1,2,3,2,1]为例说明方法二:
1、快慢指针找中点:
- 慢指针每次走1步,快指针每次走2步;
- 最终慢指针会停在节点3上。
2、反转后半部分:
- 将 [3,2,1]反转为[1,2,3];
- 此时链表的结构被修改,但比较阶段不需要完整的结构。
3、比较前后部分:
- 前半部分: [1,2,3];
- 后半部分(反转后):[1,2,3];
- 逐个比较对应节点值。
关键点说明
- 为什么在比较部分要使用 while right:后半部分长度总是整体的一部分,小于等于前半部分长度,确保只比较必要的节点对数;
- 链表的结构改变:方法二会改变链表的结构,但是比较完成后通常不需要恢复;
- 边界情况处理:空链表和单节点链表直接返回 true。
总结:
| 方法 | 优点 | 缺点 | 适用情况 |
|---|---|---|---|
| 数组+双指针 | 思路简单,不易出错 | 需要额外O(n)空间 | 对空间要求不高的场景 |
| 快慢指针+翻转 | 空间复杂度O(1) | 会修改原链表结构 | 对空间有严格要求的场景 |
通常情况下期望掌握第二种方法,因为它展示了更多的链表操作技巧。
