Java每日一题——>剑指 Offer II 027. 回文链表

Posted stormzhuo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java每日一题——>剑指 Offer II 027. 回文链表相关的知识,希望对你有一定的参考价值。

这是LeetCode上的 [027,回文链表],难度为 [简单]

题目

给定一个链表的 头节点 head ,请判断其是否为回文链表。

如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

示例 1

输入: head = [1,2,3,3,2,1]
输出: true

示例 2:

输入: head = [1,2]
输出: false

题解(快慢指针,反转)

思路分析

回文链表有个特点就是对称性,即链表的第一个结点的值和最后一个结点的值相等,以此类推。因此我们可以把链表从中间划分为两个链表,然后把前一段链表或者后一段链表反转,最后同时遍历两链表,遍历过程中比较两结点的值,若不相等,则不是回文链表,否则是回文链表

需要注意的是,题意只让我们判断链表是否是回文链表,所以我们不能改变原有链表的结构,而反转链表需要尾结点的下一个结点为null,即我们不能把前一段链表进行反转(前一段链表反转需要断开链表,即尾结点需要指向null),只能把后一段链表反转(它的尾节点是指向null的),总的来说是逻辑上划分为两个链表,并不在物理结构上断开链表

步骤

第一步(把链表在逻辑结构上从中间划分为两个链表)

要想在逻辑结构上从中间划分为两个链表,需要求后一段链表的头结点,下面分为两种情况讨论

  • 当链表长度为偶数时,链表关于中间链对称,需要从中间链中断开(逻辑结构,实际不断开),因此需要求前一段链表的尾结点,尾结点的下一个结点即为后一段链表的头结点
  • 当链表长度为奇数时,链表关于中间结点对称,需要从中间结点的左右链中断开(中间结点是共用的,所以不用比较),因此需要求链表的中间结点,中间结点的下一个结点就是后一段链表的头结点

综上,我们可以使用快慢指针来求链表的中间结点,由于当链表为偶数时,需要求前一段链表的尾结点,因此逻辑(循环终止条件)会和求中间结点稍有不同

第二步(反转后一段链表)

反转链表

第三步(同时遍历两链表,比较结点值)

第四步(再次反转后一段链表,恢复原有结构)

代码实现

public class Solution 
   
    public boolean isPalindrome(ListNode head) 
       /* 当链表为偶数时,返回前一段链表的尾结点,尾结点的下一个结点为后一段链表的头结点
        *  当链表为奇数时,返回链表的中间结点,中间结点的下一个结点为后一段链表的头结点*/
        ListNode firstHalfEndOrMiddleNode = EndOrMiddleNode(head);
        // 返回后一段链表的头结点
        ListNode secondHalfHead = firstHalfEndOrMiddleNode.next;
        Boolean aBoolean = equals(head, reverseList(secondHalfHead));
        // 恢复原有结构
        reverseList(secondHalfHead);
        return aBoolean;
    

    public ListNode EndOrMiddleNode(ListNode head) 
        ListNode slow = head;
        ListNode fast = head;
        /* 当链表长度为偶数时,终止条件为fast在链表倒数第二个结点时终止,即它的下下结点为null
        * 此时slow在前一段链表的的尾结点
        * 当链表长度为奇数时,终止条件为fast在链表的尾结点时终止,即它的下一个结点为null
        * 此时slow在链表的中间结点*/
        while (fast.next != null && fast.next.next != null) 
            slow = slow.next;
            fast = fast.next.next;
        
        return slow;
    


    public ListNode reverseList(ListNode head) 
        ListNode currNode = head;
        ListNode preNode = null;
        while (currNode != null) 
            ListNode nextNode = currNode.next;
            currNode.next = preNode;
            preNode = currNode;
            currNode = nextNode;
        
        return preNode;
    

    public Boolean equals(ListNode head1, ListNode head2) 
        while (head2 != null) 
            if (head1.val != head2.val) 
                return false;
            
            head1 = head1.next;
            head2 = head2.next;
        
        return true;
    

复杂度分析

假设l链表长度为n

时间复杂度:

最初需要遍历把链表划分为两半,时间复杂度为O(n),在反转后一段链表时,时间复杂度为O(n/2),最后同时遍历两链表时,时间复杂度为O(n/2),故总的时间复杂度为O(n) + 2O(n/2) = O(n)

空间复杂度:

只声明了几个固定的结点,故空间复杂度为O(1)

以上是关于Java每日一题——>剑指 Offer II 027. 回文链表的主要内容,如果未能解决你的问题,请参考以下文章

Java每日一题——>剑指 Offer II 035. 最小时间差(三解,蛮力,排序,哈希)

Java每日一题——> 剑指 Offer II 028. 展平多级双向链表

Java每日一题——>剑指 Offer II 027. 回文链表

Java每日一题——>剑指 Offer II 030. 插入删除和随机访问都是 O 的容器

Java每日一题——>剑指 Offer II 034. 外星语言是否排序

Java每日一题——>剑指 Offer II 034. 外星语言是否排序