【Leetcode 234 】回文链表——说实话,没写过这么详细的注释了

69 阅读2分钟

 题目

给你一个单链表的头节点 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;
}