示例 1:
输入: 1->2 输出: false
示例 2:
输入: 1->2->2->1 输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2 输出: false
示例 2:
输入: 1->2->2->1 输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
快慢指针
我们可以将链表的后半部分反转(修改链表结构),然后将前半部分和后半部分进行比较。比较完成后我们应该将链表恢复原样。虽然不需要恢复也能通过测试用例,但是使用该函数的人通常不希望链表结构被更改。
该方法虽然可以将空间复杂度降到 O(1),但是在并发环境下,该方法也有缺点。在并发环境下,函数运行时需要锁定其他线程或进程对链表的访问,因为在函数执行过程中链表会被修改。
整个流程可以分为以下五个步骤:
- 找到前半部分链表的尾节点。
- 反转后半部分链表。
- 判断是否回文。
- 恢复链表。
- 返回结果。
使用快慢指针在一次遍历中找到:慢指针一次走一步,快指针一次走两步,快慢指针同时出发。当快指针移动到链表的末尾时,慢指针恰好到链表的中间。通过慢指针将链表分为两部分。
若链表有奇数个节点,则中间的节点应该看作是前半部分。
Code
class Solution:
def findFirstHalfEnd(self, node):
fast = slow = node
while fast.next is not None and fast.next.next is not None:
fast = fast.next.next
slow = slow.next
return slow
def reverseList(self, node):
previous, current = None, node
while current is not None:
nextNode = current.next
current.next = previous
previous = current
current = nextNode
return previous
def isPalindrome(self, head: ListNode) -> bool:
# 判断边界情况
if head is None:
return True
# 找到前半部分链表的为节点并反转后半部分链表
firstHalfEnd = self.findFirstHalfEnd(head)
secondHalfStart = self.reverseList(firstHalfEnd.next)
# 判断是否为回文
ans, firstPosition, secondPosition = True, head, secondHalfStart
while ans and secondPosition is not None:
if firstPosition.val != secondPosition.val:
return False
firstPosition = firstPosition.next
secondPosition = secondPosition.next
# 还原反转的链表
firstHalfEnd.next = self.reverseList(secondHalfStart)
return ans
复杂度分析
-
时间复杂度:O(n)O(n)O(n),其中 nnn 指的是链表的大小。
-
空间复杂度:O(1)O(1)O(1)。我们只会修改原本链表中节点的指向,而在堆栈上的堆栈帧不超过 O(1)O(1)O(1)。