【LeetCode刷题】K 个一组翻转链表

给你链表的头节点 head ,每 k个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:

复制代码
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例 2:

复制代码
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

提示:

  • 链表中的节点数目为 n
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

解题思路

  1. **哨兵节点(dummy):**用一个哨兵节点作为链表的虚拟头,避免单独处理头节点翻转的边界情况,简化代码逻辑。

  2. 分组定位: 每次从当前prev节点出发,向后遍历 k 个节点,定位到当前组的尾节点end。如果遍历不足 k 个节点,说明已经到链表末尾,直接返回结果。

  3. 子链表翻转: 调用辅助函数reverse翻转当前组的子链表(从prev.nextend),该函数通过迭代方式完成单链表翻转,时间复杂度为 O (k)。

  4. 组间连接: 翻转后,将当前组的新头节点连接到prev之后,再将当前组的原头节点(翻转后的尾节点)连接到下一组的头节点,完成整个组的衔接。

  5. 迭代推进: 更新prev为当前组的原头节点(翻转后的尾节点),进入下一轮循环,处理后续节点。

复杂度分析

  • 时间复杂度:O (n),其中 n 是链表的节点数。每个节点会被访问两次(一次定位分组,一次翻转),整体复杂度为线性。
  • 空间复杂度:O (1),仅使用了常量级的额外空间,属于原地操作。

Python代码

python 复制代码
from typing import Optional


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]:
        # 哨兵节点,简化头节点处理
        dummy = ListNode(0)
        dummy.next = head
        prev = dummy

        while True:
            # 1. 检查剩余节点是否有k个
            end = prev
            for _ in range(k):
                end = end.next
                if not end:
                    return dummy.next

            # 2. 保存当前组的下一组头节点
            next_group_head = end.next
            # 3. 断开当前组与后续节点的连接
            end.next = None
            current_group_head = prev.next

            # 4. 翻转当前组
            prev.next = self.reverse(current_group_head)

            # 5. 连接翻转后的组与下一组
            current_group_head.next = next_group_head
            # 6. 更新prev为当前组的原头节点(翻转后的尾节点)
            prev = current_group_head

    # 辅助函数:翻转单链表,返回新的头节点
    def reverse(self, head: ListNode) -> ListNode:
        prev = None
        curr = head
        while curr:
            next_node = curr.next  # 暂存下一个节点
            curr.next = prev  # 翻转当前节点的指针
            prev = curr  # prev指针后移
            curr = next_node  # curr指针后移
        return prev


# 辅助函数:将列表转为链表
def list_to_linkedlist(arr):
    dummy = ListNode(0)
    curr = dummy
    for num in arr:
        curr.next = ListNode(num)
        curr = curr.next
    return dummy.next


# 辅助函数:将链表转回列表(便于打印查看结果)
def linkedlist_to_list(head):
    res = []
    curr = head
    while curr:
        res.append(curr.val)
        curr = curr.next
    return res


# 测试代码
if __name__ == "__main__":
    # 测试案例1:输入[1,2,3,4,5],k=2,预期输出[2,1,4,3,5]
    head1 = list_to_linkedlist([1, 2, 3, 4, 5])
    sol = Solution()
    reversed_head1 = sol.reverseKGroup(head1, 2)
    print("测试案例1结果:", linkedlist_to_list(reversed_head1))  # 输出 [2,1,4,3,5]

    # 测试案例2:输入[1,2,3,4,5],k=3,预期输出[3,2,1,4,5]
    head2 = list_to_linkedlist([1, 2, 3, 4, 5])
    reversed_head2 = sol.reverseKGroup(head2, 3)
    print("测试案例2结果:", linkedlist_to_list(reversed_head2))  # 输出 [3,2,1,4,5]

    # 测试案例3:输入[1],k=1,预期输出[1]
    head3 = list_to_linkedlist([1])
    reversed_head3 = sol.reverseKGroup(head3, 1)
    print("测试案例3结果:", linkedlist_to_list(reversed_head3))  # 输出 [1]

LeetCode提交代码

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]:
        # 哨兵节点,简化头节点处理
        dummy = ListNode(0)
        dummy.next = head
        prev = dummy
        
        while True:
            # 1. 检查剩余节点是否有k个
            end = prev
            for _ in range(k):
                end = end.next
                if not end:
                    return dummy.next
            
            # 2. 保存当前组的下一组头节点
            next_group_head = end.next
            # 3. 断开当前组与后续节点的连接
            end.next = None
            current_group_head = prev.next
            
            # 4. 翻转当前组
            prev.next = self.reverse(current_group_head)
            
            # 5. 连接翻转后的组与下一组
            current_group_head.next = next_group_head
            # 6. 更新prev为当前组的原头节点(翻转后的尾节点)
            prev = current_group_head
    
    # 辅助函数:翻转单链表,返回新的头节点
    def reverse(self, head: ListNode) -> ListNode:
        prev = None
        curr = head
        while curr:
            next_node = curr.next
            curr.next = prev
            prev = curr
            curr = next_node
        return prev

程序运行截图展示

总结

题目要求将链表每k个节点一组进行翻转,不足k个的保持原序。使用哨兵节点简化处理,通过分组定位、子链表翻转和组间连接实现。时间复杂度O(n),空间复杂度O(1)。Python代码通过迭代翻转每组链表,并处理边界条件,测试案例验证了正确性。例如,输入[1,2,3,4,5]和k=2时输出[2,1,4,3,5]。该方法高效且符合原地操作要求。

相关推荐
Wei&Yan1 分钟前
数据结构——顺序表(静/动态代码实现)
数据结构·c++·算法·visual studio code
lili-felicity7 分钟前
CANN模型量化详解:从FP32到INT8的精度与性能平衡
人工智能·python
数据知道10 分钟前
PostgreSQL实战:详解如何用Python优雅地从PG中存取处理JSON
python·postgresql·json
ZH154558913123 分钟前
Flutter for OpenHarmony Python学习助手实战:面向对象编程实战的实现
python·学习·flutter
玄同76523 分钟前
SQLite + LLM:大模型应用落地的轻量级数据存储方案
jvm·数据库·人工智能·python·语言模型·sqlite·知识图谱
User_芊芊君子28 分钟前
CANN010:PyASC Python编程接口—简化AI算子开发的Python框架
开发语言·人工智能·python
团子的二进制世界35 分钟前
G1垃圾收集器是如何工作的?
java·jvm·算法
白日做梦Q39 分钟前
Anchor-free检测器全解析:CenterNet vs FCOS
python·深度学习·神经网络·目标检测·机器学习
吃杠碰小鸡39 分钟前
高中数学-数列-导数证明
前端·数学·算法
故事不长丨39 分钟前
C#线程同步:lock、Monitor、Mutex原理+用法+实战全解析
开发语言·算法·c#