把这个问题拆解为三个步骤:
- 找到待翻转的区间:从当前位置开始计数,数出 kkk 个节点。
- 如果不足 kkk 个,直接返回,不翻转。
- 翻转这 kkk个节点:使用我们熟悉的"翻转链表"逻辑。
- 连接前后区间:将翻转后的局部链表重新接回原链表中
python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
prehead = ListNode(-1)
prehead.next = head
# 当前 k个节点小组的前驱节点
pre = prehead # 初始值
# 推进head
while head:
# 先组内推进,尾指针前进K个
tail = pre
for i in range(k):
tail = tail.next
if not tail: # 即使不足 k 个,也要从最开始的 prehead 返回,而不是当前组pre.next
return prehead.next
# 足k个,记录下一组的起点
nxt = tail.next
# 翻转这一小组 [head, tail]
# 定义一个子函数来处理局部翻转
head, tail = self.reverse(head, tail)
# 把翻转后的子链表接回主链表
pre.next = head
tail.next = nxt
# 指针移动,准备处理下一组
pre = tail
head = tail.next
return prehead.next
# 局部翻转辅助函数
def reverse(self, head: ListNode, tail: ListNode):
if not head or not tail:
return None
pre = None # 头节点前面的设一个空节点,先不用指向头节点,因为但反过来之后,头节点就指向none
cur = head
while pre != tail:
tmp = cur.next # 暂存后继节点 cur.next
cur.next = pre # 修改 next 引用指向
pre = cur
cur = tmp
# 翻转后,原来的 tail 变成了新头,原来的 head 变成了新尾
return tail, head
时间复杂度:O(n)O(n)O(n)
虽然有嵌套循环,但每个节点实际上只被访问了常数次(一次是定位 tail,一次是翻转)。
空间复杂度:O(1)O(1)O(1)
只额外使用了常数个指针,属于原地翻转。