【LeetCode 234】回文链表 —— 算法进阶:时间复杂度 O(n),空间复杂度 O (1)

题目:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

提示:

  • 链表中节点数目在范围[1, 105] 内

  • 0 <= Node.val <= 9

进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?


上一篇文章中我介绍了一个最简单的方法——使用集合。但这种方法会牺牲一些效率,因为 List 的操作通常比直接操作链表节点要慢,尤其是在涉及到频繁插入和删除操作时。此外,使用 List 会增加空间复杂度,因为你需要额外的空间来存储链表的元素。所以我又找到一个原地操作链表的高效算法,能实现时间复杂度为 O(n) 和 空间复杂度为 O(1) 。

首先,找到链表的中点并反转后半部分链表。接着,比较前半部分和反转后的后半部分是否相同。最后,根据比较结果返回 true 或 false。如果需要保持原链表的结构不变,可以在返回结果之前恢复链表的后半部分。

算法步骤:

  1. 找到链表的中点:使用快慢指针法,快指针每次移动两步,慢指针每次移动一步,当快指针到达链表末尾时,慢指针将位于链表的中点。

  2. 反转链表的后半部分:从中点开始反转链表的后半部分。

  3. 比较前半部分和反转后的后半部分:从链表的开始和中点开始,逐个比较节点的值,如果所有值都相同,则链表是回文的。

  4. 恢复链表(可选):如果需要保持原链表的结构不变,可以再次反转链表的后半部分以恢复原链表。

  5. 返回结果:如果链表是回文的,返回 true;否则,返回 false。

复杂度:

  • 时间复杂度:O(n),其中 n 是链表的长度。

  • 空间复杂度:O(1),算法是原地进行的,不占用额外的空间(除了几个指针变量)。

我的 Java 代码:

class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null || head.next == null) {
            return true;
        }

        // 步骤1: 找到链表的中点
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        // 步骤2: 反转链表的后半部分
        ListNode prev = null;
        while (slow != null) {
            ListNode Temp = slow.next;
            slow.next = prev;
            prev = slow;
            slow = Temp;
        }

        // 步骤3: 比较前半部分和反转后的后半部分
        ListNode firstHalf = head;
        ListNode secondHalf = prev;
        boolean isPalin = true;
        while (secondHalf != null) {
            if (firstHalf.val != secondHalf.val) {
                isPalin = false;
                break;
            }
            firstHalf = firstHalf.next;
            secondHalf = secondHalf.next;
        }

        // 步骤4: 返回结果
        return isPalin;
    }
}
posted @ 2025-07-28 17:43  junjunyi  阅读(15)  评论(0)    收藏  举报