「前端刷题」234.回文链表(EASY)

168 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

题目(Palindrome Linked List)

链接:https://leetcode-cn.com/problems/palindrome-linked-list
解决数:3046
通过率:52.5%
标签:栈 递归 链表 双指针 
相关公司:amazon microsoft facebook 

给你一个单链表的头节点 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) 空间复杂度解决此题?

思路

只要找到链表的中间位置,以中间位置为分界线,反转前半部分,再用反转了的前半部分与后半部分做对比,如有不同就返回 false。 这一种做法虽然有两次遍历,但两次遍历的长度均为链表个数的一半,所以达到时间复杂度为 O(n)O(n)

获取中间值:

设置一个中间指针 mid,在一次遍历中,head 走两格,mid 走一格,当 head 取到最后一个值或者跳出时,mid 就指向中间的值。

let mid = head
// 循环条件:只要head存在则最少走一次
while(head !== null && head.next !== null) {
    head = head.next.next // 指针一次走两格
    mid = mid.next// 中间指针一次走一格
}

反转前部分节点:

遍历的时候通过迭代来反转链表,mid 之前的 node 都会被反转。 使用迭代来反转。

while(head !== null && head.next !== null) {
        // 这个赋值要在mid被修改前提前
        pre = mid
        // 遍历链表
        mid = mid.next
        head = head.next.next
        // 反转前面部分的节点,并用reversed保存
        pre.next = reversed
        reversed = pre
    }

注意:

奇数偶数的情况略有不同,奇数情况下,在判断值是否相同时 mid 要往后走一位。

例如: 奇数:1 -> 2 -> 3 -> 2 ->1 遍历完成后:mid = 3->2->1 reversed = 2->1

偶数:1 -> 2 -> 2 ->1 遍历完成后:mid = 2->1 reversed = 2->1

完整代码如下:

var isPalindrome = function(head) {
    if(head === null || head.next === null) return true;
    let mid = head;
    let pre = null;
    let reversed = null;
    // end每次走两格,这个循环的时间复杂度为O(n/2)
    while(head !== null && head.next !== null) {
        // 这个赋值要在mid被修改前提前
        pre = mid
        // 遍历链表
        mid = mid.next
        head = head.next.next
        // 反转前面部分的节点,并用reversed保存
        pre.next = reversed
        reversed = pre
    }
    // 奇数mid往后走一位
    if(head) mid = mid.next
    while(mid) {
        if(reversed.val !== mid.val) return false
        reversed = reversed.next
        mid = mid.next
    }
    return true
};