题目
给定一个单链表 L
的头节点 head
,单链表 L
表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4]
输出:[1,4,2,3]
示例 2:
输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]
提示:
- 链表的长度范围为
[1, 5 * 10^4]
1 <= node.val <= 1000
解题
方法一:
思路
- 使用快慢指针找到链表的中间节点,并将链表分为两部分。
- 将后半部分链表反转。
- 依次将后半部分的节点插入到前半部分链表中,实现重新排列。
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reorderList(self, head: Optional[ListNode]) -> None:
if not head or not head.next:
return head
# Step 1: 快慢指针找到链表的中间节点
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 分割链表
l1 = head
l2 = slow.next
slow.next = None
# 一个问题是两半的时候怎么取? 中间那个在前在后都一样。
# Step 2: 前插反转后半部分链表
pre = None
curr = l2
while curr:
next_node = curr.next
curr.next = pre
pre = curr
curr = next_node
l2 = pre
# 注意:链表的更新,以及判断链表是否为空
# Step 3: 后插重新排列链表
# 注意:插入的时候,最后一次循环对链表的结尾是什么影响
while l2:
next_l1 = l1.next
next_l2 = l2.next
l1.next = l2
l2.next = next_l1
l1 = next_l1
l2 = next_l2
- 首先,在定义
Solution
类时,reorderList
方法接收一个名为head
的可选的ListNode
参数,并且不返回任何值。 - 在方法内部,使用条件判断
if not head or not head.next:
来检查链表是否为空或只有一个节点,如果是,则直接返回该链表。 - 接下来,定义两个指针
slow
和fast
,并将它们都指向链表的头节点head
。这里使用快慢指针的方式来找到链表的中间节点。 - 进行循环遍历,条件为
while fast and fast.next:
表示只要fast
和fast.next
都不为空,就执行循环体内的代码。在每次迭代中,将slow
指针向后移动一步,将fast
指针向后移动两步,以实现快慢指针的效果。 - 当循环结束后,快指针
fast
要么已经到达链表末尾(偶数个节点),要么已经到达倒数第二个节点(奇数个节点)。 - 然后,我们需要分割链表。将
l1
设置为链表的头节点head
,将l2
设置为慢指针slow
的下一个节点slow.next
,然后将slow.next
置为None
,即断开链表连接。 - 接下来,我们需要反转后半部分链表。定义一个前置节点
pre
并初始化为None
,定义一个当前节点curr
并初始化为l2
,然后进行迭代。- 在每次迭代中,首先获取当前节点
curr
的下一个节点next_node
。 - 然后将当前节点的
next
指针指向前置节点pre
,实现反转。 - 接着,将前置节点
pre
更新为当前节点curr
,将当前节点curr
更新为下一个节点next_node
。 - 重复上述步骤,直到当前节点
curr
为None
,即反转后半部分链表完成。
- 在每次迭代中,首先获取当前节点
- 最后,我们需要重新排列链表。使用循环遍历
l1
和l2
,条件为while l1 and l2
表示只要l1
和l2
都不为空,就执行循环体内的代码。- 在每次迭代中,首先分别保存
l1
和l2
的下一个节点,即next_l1 = l1.next
和next_l2 = l2.next
。 - 然后,将
l2
插入到l1
的后面,即将l1.next
指向l2
。 - 再将
l2.next
指向next_l1
,以将l2
连接到l1
后面。 - 最后,更新
l1
为下一个节点next_l1
,更新l2
为下一个节点next_l2
。 - 重复上述步骤,直到
l1
或l2
为None
,即重新排列链表完成。
- 在每次迭代中,首先分别保存