题目
给你一个单链表的头节点 head ,请你判断该链表是否为
回文链表
。如果是,返回 true ;否则,返回 false 。
示例 1:
输入: head = [1,2,2,1]
输出: true
示例 2:
输入: head = [1,2]
输出: false
提示:
- 链表中节点数目在范围
[1, 105]内 0 <= Node.val <= 9
时间复杂度:O(n)
空间复杂度:O(1)
题解
/** 快慢指针
* 1、找到链表的中间节点
* 2、反转链表的后半部分
* 3、比较前后部分是否相同
* 4、恢复链表的后半部分
* 5、返回结果
*/
function isPalindrome(head: ListNode | null): boolean {
if (!head) return false;
if (!head.next) return true;
// 创建快指针,其一次走两步
let fast: ListNode | null = head;
// 创建慢指针,其一次走一步,其最终通过快指针的位置将慢指针定位到链表的中间节点
let slow: ListNode | null = head;
// 1、找到链表的中间节点
// 因为快指针走的速度是慢指针的两倍,所以当快指针走到表尾时,慢指针正好在中间,其后面的元素为链表的后半部分
while (fast?.next && fast.next.next) {
//快指针一次走两步
fast = fast.next.next;
// 慢指针一次走一步
slow = slow!.next;
}
// 2、反转链表的后半部分(递归)
function reverseLinkList(head: ListNode | null): ListNode | null {
//如果head 为空(只在链表为空时,!head 条件才会成立)
// !head.next 为空,则head为尾节点,则返回head
if (!head || !head.next) return head;
//递归链表,直到最后,并保存每一个节点
const newHead: ListNode | null = reverseLinkList(head.next);
//head 节点为当前节点,将下一个节点的next指针指回上一个节点
// 如 原本是 4 -> 5 -> 6 ,head为5 ,5.next = 6
// 此操作后为 4 -> 5 -> <- 6 , 将 6.next = 5
head.next.next = head;
//由于上步操作后,5 和 6 相互指向,会成死循环,
// 此操作后 4 -> 5 <- 6
head.next = null;
// 由于递归为栈操作,所以最后返回的newHead为反转链表的表头
return newHead;
}
// 获取链表后半部分反转后的表头
const latterPartList = reverseLinkList(slow!.next) as ListNode;
// 3、比较前后部分是否相同
// 获取链表前半部分的表头
let pA: ListNode | null = head;
// 获取链表后半部分的表头
let pB: ListNode | null = latterPartList;
// 创建最终返回结果,其默认值为true,是回文链表
let result = true;
while (result && pB) {
// 当两个值不相等时,则不是回文
if (pA!.val !== pB.val) result = false;
pA = pA!.next;
pB = pB.next;
}
// 将后半部分链表恢复原样
slow!.next = reverseLinkList(latterPartList);
return result;
}