返回

234 回文链表,以别开生面的方式七天打卡

闲谈

LeetCode 的 234 回文链表问题,就是要你判断一个链表是否为回文链表,如果题目里没限制额外空间的话,那问题基本等同于字符串判断是否是回文串,按套路应该是把链表所有元素都读到数组里,再双指针比较。但是作为 leetcode 训练,要求尽可能的利用题目限制优化复杂度,甚至是优化时间复杂度。这也是我本人特别推荐的一道题,对加深理解算法相关概念和提升算法能力都很重要。

我们来一步一步拆分算法:

1、明确回文链表的性质:

  • 回文链表的特点是,正序读和倒序读都是一样的。
  • 也就是说,从头结点开始读,依次读到尾结点;从尾结点开始读,依次读到头结点,这两个过程读到的值是一样的。

2、设计算法流程:

  • 将链表分成两段,一段从头结点到中间结点,另一段从中间结点的下一个结点到尾结点。
  • 将后一段链表反转。
  • 比较两段链表的值是否相等。
  • 将后一段链表再反转回来,恢复原样。

3、算法实现:

  • 找到链表的中间结点。
  • 将链表从中间结点处断开。
  • 将后一段链表反转。
  • 比较两段链表的值是否相等。
  • 将后一段链表再反转回来,恢复原样。

4、算法分析:

  • 时间复杂度:O(n),因为我们遍历了链表两次。
  • 空间复杂度:O(1),因为我们没有使用额外的空间。

5、代码实现:

def isPalindrome(head):
    if not head or not head.next:
        return True

    # 找到链表的中间结点
    slow = head
    fast = head.next
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

    # 将链表从中间结点处断开
    mid = slow.next
    slow.next = None

    # 将后一段链表反转
    prev = None
    curr = mid
    while curr:
        next = curr.next
        curr.next = prev
        prev = curr
        curr = next

    # 比较两段链表的值是否相等
    while mid:
        if mid.val != head.val:
            return False
        mid = mid.next
        head = head.next

    # 将后一段链表再反转回来,恢复原样
    prev = None
    curr = mid
    while curr:
        next = curr.next
        curr.next = prev
        prev = curr
        curr = next

    return True

6、更优的解决方案:

上述算法的时间复杂度为 O(n),空间复杂度为 O(1)。我们可以通过使用快慢指针来找到中间结点,从而将时间复杂度降低到 O(n/2)。

def isPalindrome(head):
    if not head or not head.next:
        return True

    # 使用快慢指针找到中间结点
    slow = head
    fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

    # 将链表从中间结点处断开
    mid = slow
    slow.next = None

    # 将后一段链表反转
    prev = None
    curr = mid
    while curr:
        next = curr.next
        curr.next = prev
        prev = curr
        curr = next

    # 比较两段链表的值是否相等
    while mid:
        if mid.val != head.val:
            return False
        mid = mid.next
        head = head.next

    # 将后一段链表再反转回来,恢复原样
    prev = None
    curr = mid
    while curr:
        next = curr.next
        curr.next = prev
        prev = curr
        curr = next

    return True

这样一来,算法的时间复杂度就降到了 O(n/2)。

7、结语:

通过本文,您已经了解了如何判断一个链表是否为回文链表。我们从回文链表的性质出发,一步步推导出算法流程,并提供了两种不同实现方式。希望您能从中有所收获,并运用到实际项目中。