LeetCode刷题-链表234:回文链表

题目

 

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

解题思路

        刚拿到题目的时候第一反应是像数组那样处理,但这里的数据类型是链表,不能直接当成数组来处理。但按照这个思路,可以遍历链表,把里面的值逐个加入数组中,再判断数组是不是回文就好了。同理,还可以借助栈来实现。但这种方法需要额外消耗空间,不满足空间复杂度O(1),所以在这里就不展开了。

法一:快慢指针(执行用时:940ms,内存消耗:65.5MB)

要满足时间复杂度O(n)空间复杂度O(1)的,首先想到快慢指针。

这一块核心是双指针+反转,主要利用链表的两个操作:

(1)找到链表的中间节点

(2)反转链表

经过这两个操作,原链表将以中间节点被一分为二,前半部分是一个链表,后半部分(反转后)是一个链表。接着遍历这两个链表,如果遍历过程中发现两个链表的节点不同,说明不是回文链表,直接返回false即可。如果链表遍历完成,说明是回文链表。

需要注意的是链表长度可能是偶数,也可能是奇数。当链表长度是奇数时,后半部分长度将比前半部分多1,因此最后迭代链表时,应以前半部分为准。

注意:快慢指针不断迭代,找中间节点:

快慢指针一开始都指向链表的头节点,然后顺着链表走,快指针每次走两步,慢指针走一步,等快指针到达终止位置时,慢指针刚好走到链表中间位置。

同时用pre来记录慢指针指向节点的前一个指针,用于分割链表。

后半部分的反转可以看下这个动画(LeetCode大佬王尼玛在评论区分享的):

https://pic.leetcode-cn.com/7d8712af4fbb870537607b1dd95d66c248eb178db4319919c32d9304ee85b602-迭代.gif

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        # 当链表是空的或者只有一个节点时,都属于回文链表
        if not (head and head.next):
            return True
        #快慢指针迭代,找到链表中间节点,pre记录慢指针前一个节点
        slow,fast = head,head
        while fast and fast.next:
            pre = slow
            slow = slow.next
            fast = fast.next.next
        #分割链表,cur1是前半部分,cur2为反转后的后半部分
        pre.next = None
        cur1 = head  #这里链表已经被分割了,但链表头节点还是head
        cur2 = self.reverseList(slow)
        #前后两部分进行匹配
        while cur1:
            if cur1.val != cur2.val:
                return False
            cur1 = cur1.next
            cur2 = cur2.next
        return True
        
    #反转后半部分链表
    def reverseList(self,slow):
        cur = slow
        pre = None
        while(cur != None):
            temp = cur.next
            cur.next = pre
            pre = cur
            cur = temp
        return pre
        

这里给出官方的参考代码,也是快慢指针,但官方的代码比较简洁。(执行用时:656ms,内存消耗:48.2MB)

官方的思路是对快慢指针进行迭代的同时对前半部分进行反转,然后比较两部分内容。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def isPalindrome(self, head):
        rev, slow, fast = None, head, head
        #快慢指针迭代,同时前半部分rev进行反转
        while fast and fast.next:
            fast = fast.next.next
            rev, rev.next, slow = slow, rev, slow.next
        #定位后半部分开始节点
        if fast:
            slow = slow.next
        #比较两部分
        while slow:
            if slow.val == rev.val:
                slow = slow.next
                rev = rev.next
            else:
                return False
        return True

时间复杂度:O(n),n为链表长度

空间复杂度:O(1)

法二:递归(执行用时:1704ms,内存消耗:173.3MB)

该方法的时间复杂度和空间复杂度都是O(n),但感觉挺有意思的,也记录一下吧。

因为链表是一种兼顾迭代和递归的数据结构,所以链表可以进行先序遍历和后序遍历(树就是多叉链表)。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def isPalindrome(self, head):
        self.left = head

        def helper(right=head):
            if not right:
                return True
            res = helper(right.next)
            if self.left.val != right.val:
                return False
            self.left = self.left.next
            return res
        
        return helper()

知识补充:

在链表中最基本的 .val 表示.next 表示链表的指向(指向下一个)。关于链表的更多知识:

【Python】python链表_python 链表_小周ipython的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值