news 2026/4/17 8:20:12

链表专题(八):精细操作的巅峰——「K 个一组翻转链表」

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
链表专题(八):精细操作的巅峰——「K 个一组翻转链表」

场景想象:

你有一列很长的火车(链表),现在要把车厢按 每 K 节为一组 进行掉头。

  • 比如K=2[1, 2]掉头变成[2, 1][3, 4]掉头变成[4, 3]...

  • 关键规则:如果最后剩下的车厢不够 K 节(比如剩下个5),那就保持原样,不用掉头。

难点:

  1. 分组判断:你得先看看后面够不够 K 个,够了才翻,不够就不动。

  2. 断链与重连:翻转子链表后,原来的头变成了尾,原来的尾变成了头。你需要把它们和上一组的尾巴以及下一组的头正确地接起来。

力扣 25. K 个一组翻转链表

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

题目分析:

  • 输入:链表头head,整数k

  • 目标:每k个节点一组进行翻转。

  • 输出:修改后的链表头节点。

核心思维:虚拟头结点 + 模块化翻转

既然是重复操作,我们最好把“翻转一个子链表”这个动作封装成一个独立逻辑,或者直接在循环里复用标准的反转代码。

操作流程:

  1. Group Check:从当前位置开始向后数 K 个。如果数到了 null 还没够 K 个,说明不用翻了,直接结束。

  2. Cut & Reverse

    • 记录下这一组的开始节点start)和结束节点end)。

    • 暂时切断end.next,把[start, end]这段链表拿去反转

    • 反转后,end变成了新头,start变成了新尾。

  3. Connect

    • 上一组的尾巴pre)连到这一组的新头end)。

    • 这一组的新尾巴start)连到下一组的开头nextGroup)。

  4. Move:更新prestart(因为start已经是这组的尾巴了),准备下一轮。

代码实现 (JavaScript)

JavaScript

/** * Definition for singly-linked list. * function ListNode(val, next) { * this.val = (val===undefined ? 0 : val) * this.next = (next===undefined ? null : next) * } */ /** * @param {ListNode} head * @param {number} k * @return {ListNode} */ var reverseKGroup = function(head, k) { // 1. 虚拟头结点:因为头结点可能会变(第一组翻转后,原来的头就不是头了) const dummy = new ListNode(0, head); // pre 指向当前要处理的这组 K 个节点的前一个节点 // 初始指向 dummy let pre = dummy; let end = dummy; while (end.next !== null) { // --- 步骤一:看看剩余长度够不够 K 个 --- for (let i = 0; i < k && end !== null; i++) { end = end.next; } // 如果 end 是 null,说明不够 k 个了,直接跳出,保持原样 if (end === null) break; // --- 步骤二:准备翻转 --- // 记录关键节点 let start = pre.next; // 这一组的开头 (翻转后会变成尾) let nextGroup = end.next; // 下一组的开头 (先存起来) // 把这一组从链表中切断,方便独立翻转 end.next = null; // --- 步骤三:翻转当前组 --- // 这里直接调用我们在 LC 206 写过的反转函数 // 反转 start 到 end 这一段 pre.next = reverse(start); // --- 步骤四:重新连接 --- // 翻转后,start 变成了这一组的尾巴 // 把它连上下一组 start.next = nextGroup; // --- 步骤五:指针复位,准备下一轮 --- // pre 移动到这一组的尾巴 (也就是 start) pre = start; // end 也要复位到 pre,开始下一轮的计数 end = pre; } return dummy.next; }; // 辅助函数:标准的链表反转 (LC 206) // 输入一个头结点,返回反转后的新头结点 function reverse(head) { let pre = null; let cur = head; while (cur !== null) { let temp = cur.next; cur.next = pre; pre = cur; cur = temp; } return pre; }

深度模拟

假设1 -> 2 -> 3 -> 4 -> 5,k = 2

Round 1:

  • pre = dummy,end = dummy

  • end走 2 步 -> 指向2。够 K 个。

  • start = 1,nextGroup = 3

  • 切断:2.next = null。链表片段1 -> 2

  • 反转:变成2 -> 1

  • 连接

    • pre.next(dummy.next) 指向2

    • start.next(1.next) 指向3

    • 当前链表:dummy -> 2 -> 1 -> 3 -> 4 -> 5

  • 复位pre移到1end移到1

Round 2:

  • end走 2 步 -> 指向4。够 K 个。

  • start = 3,nextGroup = 5

  • 切断:4.next = null。片段3 -> 4

  • 反转:变成4 -> 3

  • 连接

    • pre.next(1.next) 指向4

    • start.next(3.next) 指向5

    • 当前链表:dummy -> 2 -> 1 -> 4 -> 3 -> 5

  • 复位pre移到3end移到3

Round 3:

  • end走 2 步... 哎呀,只有5一个了,不够 2 步。

  • break退出循环。

  • 最后剩下的5保持原样接在后面。

总结

这道题虽然难,但只要你引入了helper 函数 (reverse),主逻辑就会变得非常清晰。

  • 它是LC 206 (反转)的多次调用。

  • 它是Dummy Head的经典应用。

  • 它是指针断链与重连的集大成者。


下一题预告:LRU 缓存

拿下这个 Hard 之后,纯算法层面的链表题你已经通关了!🎉

最后一道压轴题 LC 146. LRU 缓存(专题九),我们不再是单纯地翻转指针,而是要设计一个数据结构。

  • 这是一个工程题,也是 Vue、React 源码里缓存机制的简化版。

  • 你需要结合HashMap双向链表,实现一个“最近最少使用”的淘汰算法,要求getput操作都是$O(1)$

准备好从“算法做题家”变身为“架构设计师”了吗?这可是前端面试的终极必考题

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 1:47:41

如何避免性能测试的常见陷阱

性能测试的核心价值与挑战 性能测试是软件质量保障的关键环节&#xff0c;旨在评估系统在高负载、高并发下的响应能力、稳定性和可扩展性。对于测试从业者而言&#xff0c;它能暴露潜在瓶颈&#xff08;如数据库延迟或代码低效&#xff09;&#xff0c;预防线上故障。然而&…

作者头像 李华
网站建设 2026/4/17 22:06:29

Dakota: Design Analysis Kit for Optimization and Terascale Applications

文章目录一、Dakota 核心功能介绍1. **优化&#xff08;Optimization&#xff09;**2. **不确定性量化&#xff08;UQ&#xff09;**3. **参数研究&#xff08;Parameter Studies&#xff09;**4. **模型校准与验证&#xff08;Calibration & Validation&#xff09;**二、…

作者头像 李华
网站建设 2026/4/18 5:05:31

放弃 IntelliJ IDEA,转 VS Code 了。。

前段时间 IntelliJ IDEA 2025.3 出了大更新&#xff0c;把免费版和收费版合并成一个软件&#xff0c;功能上还增强了&#xff0c;乍一看确实挺良心的。 但让我有点意外的是&#xff0c;看了下评论区&#xff0c;却变成了 IDEA 的大型告别现场&#xff1a; 你没看错&#xff0c…

作者头像 李华
网站建设 2026/4/15 22:48:04

[Java 并发编程] ThreadLocal 原理

ThreadLocal 原理 1. ThreadLocal 基础使用 ​ ThreadLocal 被称为线程本地变量类&#xff0c;当多线程并发操作线程本地变量时&#xff0c;实际上每个线程操作的是其独立拥有的本地值&#xff0c;可以理解为每个线程分别独立维护自己的副本。这样就规避了线程安全问题&#xf…

作者头像 李华
网站建设 2026/4/15 12:54:57

Kubernetes环境下的性能测试新范式

在云原生时代&#xff0c;Kubernetes&#xff08;K8s&#xff09;已成为容器编排的事实标准&#xff0c;它通过自动化部署、扩展和管理&#xff0c;彻底改变了应用架构。然而&#xff0c;这种动态环境对性能测试提出了独特挑战&#xff1a;容器化应用的弹性伸缩、微服务间网络延…

作者头像 李华