剑指 Offer II 027. 回文链表

76 阅读2分钟

「这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

剑指 Offer II 027. 回文链表

一、题意

image.png 剑指 Offer II 027. 回文链表

二、解法

1. 方法一:字符串拼接比较

思路:

通过正向、反向将链表节点值拼接成字符串,最后比较正向、反向字符串是否相同。

  1. 定义两个临时变量,存储正、反两个拼接的字符串
  2. 遍历链表,进行字符串拼接,正向的从前往后拼接,反向的从后往前拼接。
  3. 比较正、反字符串是否相同

代码:

function isPalindrome(head) {
    let positiveStr = '';
    let reverseStr = '';
    while (head) {
        const nodeVal = head.val;
        // 正向字符串拼接
        positiveStr += nodeVal;
        // 反向字符串拼接
        reverseStr = nodeVal + reverseStr;
        // 下一个节点
        head = head.next;
    } 
    
    return positiveStr === reverseStr;
}

复杂度分析
时间复杂度: O(n)
遍历一遍链表,时间复杂度为 O(n) 。
空间复杂度: O(1)
常数个临时变量存储,因此,空间复杂度为 O(1) 。

1. 方法二:链表反转 + 快慢指针

思路:

找到链表的中间节点,将前半部分的链表反转,与后半部分链表数据进行比较。 为了找到中间位 置,采用两个引用,步调速度相差 1,当快的引用到达最终节点时,慢的正好在中间。

  1. 分别定义快、慢指针,及前半部分的指针存储
  2. 遍历链表,快指针走 2 步,慢指针走 1 步,快指针走到链表尾部的时候,慢指针位于链表中间。
  3. 将慢指针对应的前半部分链表进行反转
  4. 前半部分从head开始遍历,后半部分从慢指针开始遍历,遍历的同时进行比较。

代码:

function isPalindrome(head) {

    // 空或者单节点
    if (!head || !head.next) {
        return true;
    }

    let slowRef = head; // 慢指针
    let fastRef = head; // 快指针
    let reverseRef; // 反转前半部分
    let reversePreRef; // 反转前一个节点
    // 连续 2 个节点都存在
    while (fastRef && fastRef.next) {
        // 快指针前进 2 步
        fastRef = fastRef.next.next;


        reverseRef = slowRef;
        // 慢指针前进 1 步
        slowRef = slowRef.next;

        // 反转链表
        reverseRef.next = reversePreRef;
        // 记录上一个节点
        reversePreRef = reverseRef;
    }

    // 奇数场景
    if (fastRef) {
        // 中间值不用比较,慢指针直接前进一步
        slowRef = slowRef.next;
    }

    while (reverseRef && slowRef) {
        // 链表逐个值比较
        if (reverseRef.val !== slowRef.val) {
            return false;
        }
        reverseRef = reverseRef.next;
        slowRef = slowRef.next;
    }
    return true;
}

复杂度分析

时间复杂度: O(n)
遍历了包含有 n 个元素的列表一次,时间复杂度为 O(n) 。
空间复杂度: O(1)
常数个临时变量存储,空间复杂度为 O(1) 。