[234] 回文链表
要判断一个链表是否是回文链表,可以使用以下步骤:
- 快慢指针找到中间节点:使用快慢指针法找到链表的中间节点。快指针每次移动两步,慢指针每次移动一步,当快指针到达链表末尾时,慢指针刚好到达链表的中间。
- 反转链表的后半部分:将链表的后半部分反转。
- 比较前半部分和反转后的后半部分:逐个比较这两部分节点的值是否相同。
- 恢复链表(可选):将反转的后半部分再反转回去,恢复原链表结构。
以下是实现代码及详细解释:
function ListNode(val) {
this.val = val;
this.next = null;
}
/**
* @param {ListNode} head
* @return {boolean}
*/
var isPalindrome = function (head) {
if (head === null || head.next === null) return true;
// 快慢指针找到中间节点
let slow = head,
fast = head;
while (fast !== null && fast.next !== null) {
slow = slow.next;
fast = fast.next.next;
}
// 反转后半部分链表
let prev = null;
while (slow !== null) {
let temp = slow.next;
slow.next = prev;
prev = slow;
slow = temp;
}
// 比较前半部分和反转后的后半部分
let left = head,
right = prev;
while (right !== null) {
if (left.val !== right.val) return false;
left = left.next;
right = right.next;
}
return true;
};
// 测试
let head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(2);
head.next.next.next = new ListNode(1);
console.log(isPalindrome(head)); // 输出 true
详细解释
找到中间节点:
- 使用两个指针
slow
和fast
。slow
每次走一步,fast
每次走两步。当fast
到达链表末尾时,slow
刚好到达中间。
let slow = head, fast = head; while (fast !== null && fast.next !== null) { slow = slow.next; fast = fast.next.next; }
- 使用两个指针
反转后半部分链表:
- 通过遍历从中间节点开始的链表,并反转其指向。
let prev = null; while (slow !== null) { let temp = slow.next; slow.next = prev; prev = slow; slow = temp; }
比较前半部分和反转后的后半部分:
- 使用两个指针分别指向前半部分的头和反转后的后半部分的头,逐个比较节点的值。
let left = head, right = prev; while (right !== null) { if (left.val !== right.val) return false; left = left.next; right = right.next; }
恢复链表(可选):
- 为了保证链表结构不变,可以将反转的部分再反转回去。这个步骤是可选的,因为它不影响判断链表是否为回文。
通过这种方法,可以在 O(n) 时间复杂度和 O(1) 空间复杂度内判断链表是否为回文。