如果是,返回 true ;否则,返回 false 。
这个思路最容易想到,但是时空复杂度高。如果直接反转整个链表然后与原链表对比的方法,虽然在反转过程中只使用了几个额外的指针变量,但反转后的链表确实占用了与原链表相同的空间。
所以这种方法的空间复杂度不是 O ( 1 ) O(1) O(1),而是 O ( n ) O(n) O(n),其中n
是链表的长度,因为存储了反转后的整个链表。
一、时间复杂度分析
反转链表和对比两个链表都需要遍历整个链表一次,总的时间复杂度为 O ( n ) + O ( n ) = O ( n ) O(n)+O(n)=O(n) O(n)+O(n)=O(n)。
二、空间复杂度分析
在反转链表过程中,生成了一个与原链表长度相同的反转后的链表,所以空间复杂度为 O ( n ) O(n) O(n)。
三、 Python 代码及分析:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head):
# 迭代法反转链表
prev = None
curr = head
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
# 对比反转后的链表和原链表
reversed_head = prev
original_head = head
is_palindrome = True
while original_head and reversed_head:
if original_head.val!= reversed_head.val:
is_palindrome = False
break
original_head = original_head.next
reversed_head = reversed_head.next
# 再次反转链表恢复原状(可选)
prev = None
curr = reversed_head
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
return is_palindrome
运行结果:解答错误 79 / 93 个通过的测试用例
在这个实现中,反转链表后占用了额外的空间来存储反转后的链表,所以空间复杂度为 O ( n ) O(n) O(n)。而时间复杂度仍然是 O ( n ) O(n) O(n),因为需要遍历链表两次,一次用于反转,一次用于对比。
递归的思路反转原始链表再对比来判断是否为回文链表,可以这样分析:
一、时间复杂度
n
是链表的长度。二、空间复杂度
n
,因此递归调用栈的空间复杂度为 O ( n ) O(n) O(n)。所以总体空间复杂度为 O ( n ) O(n) O(n)。
下面是一个用递归反转链表并对比判断回文链表的 Python 代码示例:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head):
self.original_head = head
reversed_head = self.reverseListRecursive(head)
is_palindrome = True
while head and reversed_head:
if head.val!= reversed_head.val:
is_palindrome = False
break
head = head.next
reversed_head = reversed_head.next
return is_palindrome
def reverseListRecursive(self, head):
if not head or not head.next:
return head
new_head = self.reverseListRecursive(head.next)
head.next.next = head
head.next = None
return new_head
上面的代码有个错误!
对比阶段!
这个问题是因为在比较的时候,没有考虑到原始链表和反转后的链表在比较过程中需要同时移动,而不是只移动一个链表。修改后的代码如下:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head):
self.original_head = head
reversed_head = self.reverseListRecursive(head)
is_palindrome = True
while reversed_head:
if head.val!= reversed_head.val:
is_palindrome = False
break
head = head.next
reversed_head = reversed_head.next
return is_palindrome
def reverseListRecursive(self, head):
if not head or not head.next:
return head
new_head = self.reverseListRecursive(head.next)
head.next.next = head
head.next = None
return new_head
AttributeError: 'NoneType' object has no attribute 'val'
^^^^^^^^
if head.val!= reversed_head.val:
Line 8 in isPalindrome (Solution.py)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ret = Solution().isPalindrome(param_1)
Line 42 in _driver (Solution.py)
_driver()
Line 53 in <module> (Solution.py)
这个错误是因为在比较时,head
可能已经为None
,但仍然尝试访问其val
属性。这通常发生在链表长度为奇数的情况下,当比较完中间节点后,继续比较时head
已经到达链表末尾,为None
。
可以在比较循环中添加额外的条件检查来避免这个错误:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head):
self.original_head = head
reversed_head = self.reverseListRecursive(head)
is_palindrome = True
while reversed_head:
if not head:
break
if head.val!= reversed_head.val:
is_palindrome = False
break
head = head.next
reversed_head = reversed_head.next
return is_palindrome
def reverseListRecursive(self, head):
if not head or not head.next:
return head
new_head = self.reverseListRecursive(head.next)
head.next.next = head
head.next = None
return new_head
在这个版本中,只在比较循环中检查反转后的链表是否为空,确保原始链表和反转后的链表同时移动并进行比较。
综上所述,使用递归反转链表再对比的方法,时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)。这种方法虽然在实现上比较直观,但由于空间复杂度较高,在实际应用中可能不是最优的选择。
思路分析:
代码实现思路:
slow
和fast
,初始都指向链表的头节点。fast
和fast.next
都不为空时,slow
和fast
分别向前移动一步和两步。prev
、curr
和next_node
,逐个改变节点的指针方向。Python 代码示例:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head):
slow = fast = head
# 找到链表中点
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 反转后半部分链表
prev = None
curr = slow
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
# 比较前半部分和反转后的后半部分
first_half = head
second_half = prev
while second_half:
if first_half.val!= second_half.val:
return False
first_half = first_half.next
second_half = second_half.next
return True
当链表长度为奇数时,slow
确实会停在中点位置,但是不会导致重复对比。
原因如下:
2n + 1
,前半部分有n
个节点,后半部分也有n
个节点(不包括中间节点)。例如,对于链表1 -> 2 -> 3 -> 2 -> 1
,长度为 5(奇数)。
slow
指向值为3
的节点。1 -> 2 -> 3 <- 2 <- 1
。1
的节点开始,后半部分从值为1
的节点开始,当后半部分遍历完两个节点后,前半部分也正好遍历到值为3
的节点的前一个节点,不会重复比较中间节点3
。