LeetCode 25. K 个一组翻转链表 (迭代+分组反转法)

1. 题目简述(题目链接)

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

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

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

2. 核心实现方法:迭代 + 分组反转

这道题的最优解法是使用迭代法 配合虚拟头节点 (Dummy Node)。整体思路分为两步:

  1. 分组探路 :从当前组的起点向后走 k 步,如果能走满 k 步,说明这一组需要翻转;如果不足 k步,说明剩余节点保持原序即可,直接结束。
  2. 原地反转并拼接:在组内进行标准的链表反转,但需要注意将反转后的尾部与下一组的头部重新连接。

3.推演 (k=3 )

为了彻底讲清楚反转过程,我们避开简单的 k=2,采用一个更复杂的例子

复制代码
链表:1 -> 2 -> 3 -> 4 -> 5,k = 3

3.1 初始状态与指针定义

我们先定义几个关键指针:

  • dummy:虚拟头节点,指向 1。
  • groupPrev:指向当前待翻转组的前一个节点。初始时 groupPrev = dummy。
  • kth:用来探路,寻找当前组的第 k个节点。
  • groupNext:当前组翻转后,应该连接的下一个节点。

初始链表状态:

dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> null

groupPrev 指向 dummy。

3.2 第一轮循环:翻转 1, 2, 3

Step 1: 探路寻找第 k 个节点

从 groupPrev 出发向后走 3 步,找到 kth = 3。

此时,groupNext = kth->next = 4。(记录下下一组的起点)

Step 2: 核心反转逻辑

我们使用 prev 和 curr 两个指针进行局部反转。

关键技巧:让 prev 初始指向 groupNext(即 4),curr 初始指向当前组的头节点(即 1)。

这样做的目的是:在反转的过程中,顺带把当前组的尾节点直接连到下一组的头上,省去后续的拼接步骤。

初始:prev = 4, curr = 1

第 1 次循环:

temp = curr->next (temp = 2)

curr->next = prev (1 -> 4)

prev = curr (prev = 1)

curr = temp (curr = 2)

此时局部状态: 1 -> 4*,* 2和 3还没处理。

第 2 次循环:

temp = curr->next (temp = 3)

curr->next = prev (2 -> 1)

prev = curr (prev = 2)

curr = temp (curr = 3)

此时局部状态: 2 -> 1 -> 4*。*

第 3 次循环:

temp = curr->next (temp = 4,即 groupNext)

curr->next = prev (3 -> 2)

prev = curr (prev = 3)

curr = temp (curr = 4)

此时局部状态: 3 -> 2 -> 1 -> 4*。*

Step 3: 循环结束与拼接

当 curr == groupNext (即 4) 时,反转循环结束。

此时,prev 指向了反转后的新头节点 3。

将前驱节点连上新头:groupPrev->next = prev; (dummy -> 3)

更新 groupPrev 为下一轮的前驱节点:groupPrev = temp; (注意这里的 temp 是第一次循环前保存的 1,即反转后的尾节点)。groupPrev 现在指向1。

第一轮结束后的链表状态:

dummy -> 3 -> 2 -> 1 -> 4 -> 5 -> null

groupPrev 指向 1。

3.3 第二轮循环:处理剩余节点 4, 5

Step 1: 探路失败

从 groupPrev (即 1) 出发向后走 3 步:

第 1 步到 4,第 2 步到 5,第 3 步发现 kth 变成了 nullptr。

因为不足 3 个节点,说明剩余节点不需要翻转。

Step 2: 退出循环

直接 break 跳出大循环。

3.4 最终结果

返回 dummy->next,得到最终链表:

3 -> 2 -> 1 -> 4 -> 5

4. 代码实现

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        // 创建虚拟头节点,避免处理头节点翻转的特殊情况
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        
        // groupPrev 指向当前要翻转的 k 个节点的前一个节点
        ListNode* groupPrev = dummy;

        while (true) {
            // 1. 检查剩余节点是否足够 k 个
            ListNode* kth = groupPrev;
            for (int i = 0; i < k && kth != nullptr; ++i) {
                kth = kth->next;
            }
            // 如果不足 k 个,说明后面的不用翻转了,直接退出
            if (kth == nullptr) {
                break;
            }

            // 2. 记录下一组的起始节点
            ListNode* groupNext = kth->next;

            // 3. 翻转当前组的 k 个节点
            // 核心技巧:prev 初始指向 groupNext,方便反转后直接连上后面
            ListNode* prev = groupNext; 
            ListNode* curr = groupPrev->next;
            while (curr != groupNext) {
                ListNode* temp = curr->next;
                curr->next = prev;
                prev = curr;
                curr = temp;
            }

            // 4. 连接翻转后的部分与前面的链表
            ListNode* temp = groupPrev->next; // 原来的头节点,现在是尾节点
            groupPrev->next = prev;           // 前驱节点指向新头
            groupPrev = temp;                 // groupPrev 移动到当前组的尾部,准备下一轮
        }

        return dummy->next;
    }
};

5. 复杂度分析

时间复杂度:O(N)。每个节点最多被遍历两次(一次是探路寻找 kth,一次是反转操作),整体呈线性时间。

空间复杂度:O(1)。只使用了常数个额外的指针变量,满足题目"实际进行节点交换"且不消耗额外空间的要求。

相关推荐
xuxigifxfh1 小时前
牛客:BISHI8 大整数哈希
java·算法·哈希算法
涉密IT资质笔记1 小时前
涉密资质投入产出分析模型:时间成本、四块成本构成与 ROI 决策框架
java·开发语言·算法
木井巳1 小时前
【BFS 解决拓扑排序】火星词典
java·算法·leetcode·广度优先·宽度优先·图搜索算法
北风toto1 小时前
KMP 算法详解:从暴力匹配到 O(n+m) 的字符串搜索
java·开发语言·算法
林澈在路上1 小时前
哪个写歌软件可以用自己声音?MELO音乐详解
人工智能·python·算法
爱读书的小胖1 小时前
Codewars Sum of a Sequence Hard Core Version C 解题分析
c语言·数据结构·算法
不会就选b1 小时前
算法日常・每日刷题--<动态规划>24
算法·动态规划
Shaoshing2 小时前
二分模板(左右查询)
java·算法·二分
蒲公英eric2 小时前
线性回归与正则化:从最小二乘到岭回归、LASSO
人工智能·算法·数学建模·线性回归·岭回归·最小二乘·lasso