题目描述
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:

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

输入:head = [1,2]
输出:false
提示:
- 链表中节点数目在范围 [1,105][1, 10^5][1,105] 内
- 0 <= Node.val <= 9
进阶:你能否用 O(n)O(n)O(n) 时间复杂度和 O(1)O(1)O(1) 空间复杂度解决此题?
思考
判断回文链表有两种典型思路,需根据「空间复杂度」需求选择,核心是利用回文“前后对称”的特性:
-
数组辅助法(O(n) 时间 + O(n) 空间)
先遍历链表,将所有节点值存入数组,再用「相向双指针」(左指针从数组头部、右指针从数组尾部)逐位对比值是否相等。若全程相等则为回文,否则不是。
优势是逻辑直观、实现简单;劣势是需额外存储数组,空间复杂度较高,不适用于内存敏感场景。 -
快慢指针+反转链表法(O(n) 时间 + O(1) 空间)
这是满足「常数空间」的最优解,核心是用链表操作替代额外存储:- 利用「快慢指针」找链表中点,将链表分为前半段和后半段;
- 反转后半段链表,使后半段能“从后往前”与前半段对比;
- 用「双指针」分别遍历前半段和反转后的后半段,逐位对比值;
- (可选)对比结束后反转后半段并接回原链表,恢复链表结构。
优势是无额外空间消耗,时间复杂度仍为 O(n);劣势是逻辑稍复杂,需处理链表反转和中点定位的边界。。
算法过程
- 找中点:初始化快慢指针均指向头节点,快指针每次走2步、慢指针每次走1步,直至快指针无法再走2步,此时慢指针指向前半段尾节点,后半段从慢指针.next开始;
- 反转后半段:迭代反转慢指针.next后的链表,得到反转后后半段的表头;
- 双指针对比:左指针从原链表头出发,右指针从反转后后半段表头出发,逐节点对比值,若不相等则非回文;
- 返回结果:若右指针遍历完仍未出现不相等,返回true(可选:反转后半段恢复原链表结构)。
代码
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var isPalindrome = function(head) {
if (!head.next) return true;
let slow = head, fast = head;
while (fast.next && fast.next.next) {
slow = slow.next;
fast = fast.next.next;
}
slow = reverseLink(slow.next);
let node = head;
while (slow) {
if (node.val !== slow.val) return false;
node = node.next;
slow = slow.next;
}
return true;
};
function reverseLink(head) {
if (!head) return head;
let headNode = head;
let node = head;
while (node.next) {
let tmp = node.next;
node.next = tmp.next;
tmp.next = headNode;
headNode = tmp;
}
return headNode;
}
988

被折叠的 条评论
为什么被折叠?



