LeetCode 25:K 个一组反转链表

文章内容

https://leetcode.cn/problems/reverse-nodes-in-k-group/description/

一、题目解析

1.1 题意

给定链表头节点 head 和整数 k,每 k 个节点一组进行反转,返回反转后的链表。如果最后一组不足 k 个,保持原顺序。

plain text
输入:1→2→3→4→5, k=2输出:2→1→4→3→5
plain text
输入:1→2→3→4→5, k=3输出:3→2→1→4→5   (最后两个不足 3 个,不动)

核心操作:把链表按 k 个切成段,每段递归反转,再串回去。

K 个一组反转链表

二、递归反转模板

先看单链表反转的递归写法——后续分段反转的基础组件。

java
private ListNode reverse(ListNode head) {    // 空链表或到尾节点,直接返回    if (head == null || head.next == null) {        return head;    }    // 递归反转后续链表    ListNode next = reverse(head.next);    // 让后继节点回头指向自己    head.next.next = head;    // 断开原指针    head.next = null;    return next;}

反转后,原来的头变成尾(next 为 null),原来的尾变成头(被返回),后面接回主链时会反复用到。

三、分段处理框架

reverseKGroup 的骨架是一个标准的链表分段循环,配合 dummy 节点省掉头节点特判:

plain text
dummy → [段1] → [段2] → [段3] → ...  ↑ prev(指向已处理部分的尾部)

每轮循环做四步:

  1. 找当前段:segHead = prev.next(段头),segTail 从 prev 出发走 k 步(段尾)。不足 k 个直接 break。
  2. 切断:segTail.next = null,把当前段从主链上拆下来。
  3. 反转:调 reverse(segHead),原来的 segHead 变尾,segTail 变头。
  4. 接回:segHead.next = nextHead(旧头接下一段),prev.next = segTail(prev 接新头),prev = segHead(prev 移到当前段新尾)。

分段处理框架

四、代码详解

java
class Solution {    // 递归反转单链表,返回新头    private ListNode reverse(ListNode head) {        if (head == null || head.next == null) {            return head;        }        ListNode next = reverse(head.next);        head.next.next = head;        head.next = null;        return next;    }     public ListNode reverseKGroup(ListNode head, int k) {        // dummy 哨兵,统一头节点操作        ListNode dummy = new ListNode(-1);        dummy.next = head;        ListNode prev = dummy;          // 指向已处理部分的最后一个节点         while (prev.next != null) {            // Step 1:找当前段            ListNode segHead = prev.next;   // 当前段的头            ListNode segTail = prev;        // 从 prev 开始往后走 k 步找尾            int count = 0;            while (segTail.next != null && count < k) {                count++;                segTail = segTail.next;            }            // 不足 k 个,不处理            if (count < k) {                break;            }             // Step 2:暂存下一段的头,然后切断当前段            ListNode nextHead = segTail.next;            segTail.next = null;             // Step 3:递归反转当前段            // 注意:reverse 之后 segHead 变成尾,segTail 变成头            ListNode reversed = reverse(segHead);             // Step 4:接回主链            // segHead(旧头,新尾)的 next 指向下一段            segHead.next = nextHead;            // prev 的 next 指向 segTail(旧尾,新头)            prev.next = segTail;            // prev 前进到当前段的新尾(segHead)            prev = segHead;        }        return dummy.next;    }}
Question

Q:为什么 segTail 变成新头?
A:reverse(segHead) 把从 segHead 到 segTail 这段链表完全反转。反转前 segHead 是头、segTail 是尾;反转后 segHead 变成尾(next 指向 null,也就是之前切断的位置),而 segTail 的 next 被上层递归改成了指向它在原链表中的前驱,最终 segTail 成为新头。所以接回时 prev.next = segTail 把新头接上,segHead.next = nextHead 把新尾接上。

prev 的推进。 处理完一段后,prev = segHead。此时 segHead 是当前段反转后的尾节点,它同时是下一段的前一个节点。下轮循环 prev.next 取到的正好是下一段的头。

不足 k 个时直接 break。 题目要求最后一组不足 k 个保持原样。break 之后 segTail.next = null 那步没执行,剩余节点仍通过 prev.next 连着主链,不会被拆散。

五、复杂度分析

时间 O(n)。每个节点恰好被遍历两轮:一轮在 while 循环里找段尾(count 计数),一轮在 reverse 递归里翻转指针。总操作次数约 2n。

空间 O(k)。递归反转最深 k 层,调用栈深度为 k。


本文由 AgentPlanFlow 生成

觉得不错?给作者点个赞吧

分类:题解

评论 (0)

无需注册,留言经站长审核后公开。

加载评论中…

LeetCode 25:K 个一组反转链表 · Yinchen 的博客