news 2026/4/3 3:07:32

链表专题(八):精细操作的巅峰——「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/1 3:21:00

水电站大坝安全远程监测系统方案

某水电站进行数字化改造&#xff0c;需要对大坝进行远程监测&#xff0c;包括变形监测、渗流监测、应变应力监测等内容&#xff0c;以替代人工巡检抄表&#xff0c;确保大坝安全&#xff1b;同时需要对服务器机房进行监测&#xff0c;包括环境温湿度、电压电流、UPS状态等&…

作者头像 李华
网站建设 2026/4/2 8:34:14

边缘模型增量微调实战

&#x1f493; 博客主页&#xff1a;借口的CSDN主页 ⏩ 文章专栏&#xff1a;《热点资讯》 边缘模型增量微调实战&#xff1a;从理论到边缘设备的高效部署目录边缘模型增量微调实战&#xff1a;从理论到边缘设备的高效部署 引言&#xff1a;边缘智能的必然选择 一、现在时&…

作者头像 李华
网站建设 2026/3/28 9:22:37

测试人员心理安全:勇于报错

引言&#xff1a;被忽视的质量防线 2025年ISTQB全球测试现状报告显示&#xff0c;68%的线上事故源于未被上报的已知缺陷。某金融科技公司事故复盘中发现&#xff0c;测试工程师王某早在预发环境捕获到支付链路超时问题&#xff0c;但因担心被开发团队指责"过度敏感"…

作者头像 李华