234. 回文链表
请判断一个链表是否为回文链表。
输入: 1->2->2->1 输出: true
一.反转后半部分
1. 分析
寻找回文串是从中间向两端扩展,判断回文串是从两端向中间收缩
那么,此题是从两边向中间收缩,但是对于单链表,是不能倒序遍历的.
- 通过反转链表,然后再遍历对比,由于反转后原先的链表指向发生变化,涉及到深拷贝,此路不通
- 通过将所有节点存入列表,再进行判断,可行,此处不展示
通过反转后半部分链表,再进行对比
2. 解决问题
-
反转后半部分链表,最主要的是找到
中间节点
通过
快慢指针
可解决:定义两个指针,快指针每次走两步,慢指针每次走一步,那么,当快指针结束时,慢指针就是中间节点slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next
考虑到存在奇偶两种情况:
- 当链表长度为奇数时: 1->2->3->2->1, 慢指针指向的就是3节点
- 当链表长度为奇数时: 1->2->3->33->2->1, 慢指针指向的就是33节点:
-
找到中间节点后反转链表
def reverse(self, head): pre = None cur = head while cur: nxt = cur.next cur.next = pre pre = cur cur = nxt return pre
考虑到存在奇偶两种情况:
-
当链表长度为奇数时: 1->2->3->2->1, 慢指针指向的就是3节点,反转后半部分链表后:
前半部分:1->2->3->None
后半部分:1->2->3->None
-
当链表长度为奇数时: 1->2->3->
3
->2->1, 慢指针指向的就是3
节点,反转后半部分链表后:前半部分:1->2->3->
3
->None后半部分:1->2->
3
->None
-
-
将前后两段进行对比,考虑到奇偶两种情况,停止情况安装后部分指针到达None时停止
while right: if head.val != right.val: return False head = head.next right = right.next return True
3.完整代码
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def isPalindrome(self, head: ListNode):
# 找到指针中点
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
right = self.reverse(slow)
while right:
if head.val != right.val:
return False
head = head.next
right = right.next
return True
def reverse(self, head):
pre = None
cur = head
while cur:
nxt = cur.next
cur.next = pre
pre = cur
cur = nxt
return pre
if __name__ == '__main__':
head = ListNode(1)
head.next = ListNode(2)
head.next.next = ListNode(3)
head.next.next.next = ListNode(3)
head.next.next.next.next = ListNode(2)
head.next.next.next.next.next = ListNode(1)
so = Solution()
rs = so.isPalindrome(head)
print(rs)
二. 反向遍历链表方式
1. 分析
单链表是无法进行倒序遍历的
但是,我们可以参考二叉树的遍历方法:
# 递归思想 ---- 二叉树的遍历方式
def traverse(root):
# 这里写前序遍历代码
traverse(root.left)
# 这里写中序遍历代码
traverse(root.right)
# 这里写倒序遍历代码
那么链表参考后
def traverse(head):
# 这里写前序遍历代码
traverse(head.next)
# 这里写倒序遍历代码
测试: 输入链表 head = 1->2->3->4->5->6>None
前序(正序):
def traverse(head): if not head: return print(head.val) traverse(head.next) # 输出为 1,2,3,4,5,6
倒序:
def traverse(head): if not head: return traverse(head.next) print(head.val) # 输出为 6,5,4,3,2,1
这么做的核心逻辑就是**把节点每次递归时放入了栈中,然后再取出,**顺序就是反的
2.解决问题
- 定义一个递归函数traverse(right),使用上述的递归思想,那么参数right就是逆序的最后一个节点.
- 定义一个外部变量left,为链表的起始节点
- traverse(right)的意义就是right节点和left节点的值是否相等,不相等则返回False,相等返回True
- 当递归到达base case时,说明一直没有返回,则返回True
伪代码:
left = head
def traverse(right):
if not rigth: return True
res = traverse(right.next)
if left.val != right.val:
return False
left = left.next
return res
3.完整代码
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
self.left = head
def traverse(right):
if not right:
return True
res = traverse(right.next)
if self.left.val != right.val:
return False
self.left = self.left.next
return res
return traverse(head)
if __name__ == '__main__':
head = ListNode(1)
head.next = ListNode(2)
head.next.next = ListNode(3)
head.next.next.next = ListNode(3)
head.next.next.next.next = ListNode(2)
head.next.next.next.next.next = ListNode(1)
so = Solution()
rs = so.isPalindrome(head)
print(rs)