刷题随记 - 4. 回文链表

LeetCode 234 难度 easy

参考:公众号 labuladong 如何高效判断回文单链表?

插播:

  • 快慢指针法可以更快速地判断一个链表、数组等的长度是奇数还是偶数,都不用进行遍历一遍,不用使用 长度模 2 来判断。
  • 直接判断 fast指针是否为空即可
  • 为空,则为 偶数
  • 不为空 奇数

在这里插入图片描述

判断回文链表

①隐式地调用系统的栈来实现双指针

神奇的思路,利用 链表的后序遍历框架

在这里插入图片描述
链表不像字符串一样,可以使用下标迭代,所以,无法使用双指针技巧,头尾一起迭代比较。

因此,将链表全部反转,然后进行比较,是一种方法。

但是,可以不用显式地反转链表

使用内存中的栈,来进行后序遍历。

神奇!运用了内存中的栈,实现后序遍历,实现从尾部读取的效果!!

对比二叉树的遍历框架:

void traverse(TreeNode root) {
    // 前序遍历代码
    traverse(root.left);
    // 中序遍历代码
    traverse(root.right);
    // 后序遍历代码
}

由于链表也是递归结构,因此,也一样有遍历框架

void traverse(ListNode head) {
    // 前序遍历代码
    traverse(head.next);
    // 后序遍历代码
}

这个框架有什么指导意义呢?如果我想正序打印链表中的val值,可以在前序遍历位置写代码;反之,如果想倒序遍历链表,就可以在后序遍历位置操作:

/* 倒序打印单链表中的元素值 */
void traverse(ListNode head) {
    if (head == null) return;
    traverse(head.next);
    // 后序遍历代码
    print(head.val);
}

因此,可以使用后序遍历框架,模拟头尾指针的操作!!

// 注意,要使用双指针,这里要定义全局变量 left,这样在递归的时候正确使用
let left

var isPalindrome = function(head) {
    left = head
    return postorderTravel(head.next)
};

function postorderTravel(right){
    if(right == null){
        return true
    }
	// 后序遍历框架
    let result = postorderTravel(right.next)
    
    result = result && (left.val === right.val)
    left = left.next
    return result
}

在这里插入图片描述

②优化时间复杂度
1.使用快慢指针找链表的中点(也是判断奇偶的好方法)
let slow = head
    let fast = head
    while(fast != null && fast.next != null){
        slow = slow.next
        fast = fast.next.next
    }

在这里插入图片描述

2.如果fast没有指向null的话,链表长度为奇数,slow还需要往前走一步
    // 如果是奇数的情况下,fast不为空,slow在中心位置,需要再往前走一个位置
    if(fast != null){
        slow = slow.next
    }

在这里插入图片描述

3.从slow开始反转后面的链表,现在就可以开始比较回文串了:
    let left = head
    let end = reverseList(slow)
    let right = end
    let begin = null // 记录slow之前的那个位置
    while(right != null){
        if(left.val !== right.val){
            return false
        }
        if(right.next == null){
            begin = left
        }
        left = left.next
        right = right.next
    }

    // 复原
    begin.next = reverseList(end)

    return true

在这里插入图片描述

4.复原链表
// 找到slow之前的那个位置,记录下来,为了后面复原
// right再往前走是空的时候,left再往前走就是slow,所以提前记录
// if(left.next === slow) 跑不通,也就是说 JS 中指针的值无法比较
        if(right.next == null){
            begin = left
        }

在这里插入图片描述

5. 代码书写

由于没有判断特殊情况,导致报错

    if(head == null || head.next == null){
        return true
    }

在这里插入图片描述

一样的,反转链表中没有判断特殊情况 head.next == null ,报错

    if(head == null || head.next == null){
        return head
    }

在这里插入图片描述

function reverseList(head){
    if(head == null || head.next == null){
        return head
    }
    const last = reverseList(head.next)
    head.next.next = head
    head.next = null
    return last
}

var isPalindrome = function(head) {
    if(head == null || head.next == null){
        return true
    }
    let slow = head
    let fast = head
    while(fast != null && fast.next != null){
        slow = slow.next
        fast = fast.next.next
    }
    // 如果是奇数的情况下,fast不为空,slow在中心位置,需要再往前走一个位置
    if(fast != null){
        slow = slow.next
    }

    let right = reverseList(slow)
    let begin = null
    let end = right
    let left = head
    while(right != null){
        if(left.val != right.val){
            return false
        }
        // 找到slow之前的那个位置,记录下来,为了后面复原
        // right再往前走是空的时候,left再往前走就是slow,所以提前记录下来
        // if(left.next === slow) 跑不通,也就是说 JS 中指针的值无法比较
        if(right.next == null){
            begin = left
        }
        left = left.next
        right = right.next
    }
    // 复原
    begin.next = reverseList(end)

    return true
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值