前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库k个一组反转链表
数据数据结构链表栈队列

如何每 k 个节点一组反转链表,并处理末尾不足 k 个的情况?

按 k 个一组反转链表需要先定位每组边界,反转后拼回;末尾不足 k 个时保持原始顺序。

前端进阶之旅 · 一题精讲更新于 2026.09.05
数据结构#链表栈队列#算法
先看核心答案读代码示例
理解线索

分组反转的拼合机制

  1. 计数确定边界每次前进 k 步,不足则停止
  2. 反转区间指针保存组前驱和组后继,头插反转
  3. 更新连接点每组反转后把前驱指向新头

当剩余节点数少于 k 时,不执行反转,保持原序

核心回答

先记住这个答案

先用哑节点固定头位置,遍历统计每组是否有 k 个节点;若满 k 个则反转该子链表并接回,否则停止。实现时每个子链表反转前记录前驱和后继,反转完成后更新指针;时间复杂度 O(n),额外空间 O(1)。

  • 先检查剩余节点数是否满 k
  • 反转子链表需保存前驱后继
  • 剩余不足k个保持原序

分组反转的核心流程

采用哑节点简化头部处理。用指针 prev 指向待处理组的前驱,每次先走 k 步检查是否存在 k 个节点:若中途遇空,说明不足,直接返回 dummy.next。若足够,则记录该组起点 start 和组后第一个节点 nextGroup。

反转 start 到 nextGroup 之前的子链表,此时 start 会成为该组的新尾部。将 prev.next 指向反转后的新头,同时把 start.next 接在 nextGroup 上。随后更新 prev 为 start,继续下一轮,直到后续剩余不足 k。

迭代实现 k 个一组反转JavaScript
function reverseKGroup(head, k) {
  const dummy = new ListNode(0, head);
  let prev = dummy;
  while (prev) {
    let groupHead = prev.next;
    let end = prev;
    // 检查是否还有 k 个节点
    for (let i = 0; i < k && end; i++) end = end.next;
    if (!end) break;           // 不足 k 个,保留原序
    const nextGroup = end.next;
    // 反转 groupHead 到 end 之间的子链表
    let cur = groupHead;
    let last = null;
    let temp = null;
    while (cur !== nextGroup) {
      temp = cur.next;
      cur.next = last;
      last = cur;
      cur = temp;
    }
    // 此时 last 是反转后的新头,groupHead 变成新尾
    prev.next = last;
    groupHead.next = nextGroup;
    prev = groupHead;
  }
  return dummy.next;
}

// 辅助测试用
function toArray(head) {
  const res = [];
  while (head) { res.push(head.val); head = head.next; }
  return res;
}
function fromArray(arr) {
  const dummy = new ListNode();
  let tail = dummy;
  for (const val of arr) { tail.next = new ListNode(val); tail = tail.next; }
  return dummy.next;
}
function ListNode(val, next) {
  this.val = (val===undefined ? 0 : val);
  this.next = (next===undefined ? null : next);
}

// 测试:k=3,链表长度 5 -> 最后不足 3 保留
console.log(JSON.stringify(toArray(reverseKGroup(fromArray([1,2,3,4,5]), 3))));
// 预期输出 [3,2,1,4,5]

// 测试:k=2,链表长度 3 -> 最后不足 2 保留
console.log(JSON.stringify(toArray(reverseKGroup(fromArray([1,2,3]), 2))));
// 预期输出 [2,1,3]

// 测试:k=1,所有组都只有1个,无反转
console.log(JSON.stringify(toArray(reverseKGroup(fromArray([1,2,3]), 1))));
// 预期输出 [1,2,3]
查看输出与解释
[3,2,1,4,5]
[2,1,3]
[1,2,3]

该代码在 Node.js 中直接运行,输出为上述结果。每轮循环先检查是否有 k 个节点,确认后反转子链表,再调整前后连接,时间复杂度 O(n),空间 O(1)。

日志序列按批倒序处理

假设有一个日志列表,按时间顺序存储,每批最多处理 3 条,要求批内倒序,最后不足 3 条则不打乱。例如有 5 条日志 [L1,L2,L3,L4,L5],应输出 [L3,L2,L1,L4,L5],因为 [L4,L5] 不足 3 条不反转。

使用分组反转算法,先检查每批是否有 3 个节点。如果有则批内反转,并维护好相邻批次的链指针;如果没有则直接返回当前头。这样只需要一次遍历且不修改节点值,通过调整指针实现反转,避免复制节点数据。

边界判定与失败条件

最容易出错的是 k 等于链表长度或 k 大于长度。当 k 大于长度时,第一次检查就会遇到 null,循环不会执行,直接返回原链表,正确。当 k 等于长度时,整个链表反转一次,最后 prev 会移动到原链表尾,循环再次检查时剩余 0 个节点,也不反转,结果正确。

另一个风险是反转操作中的指针错乱:反转子链表时若错误修改 nextGroup 前的连接,会导致后半部分丢失。因此必须记录 nextGroup 并在反转后立刻连接。此外,当 k=1 时,循环每次检查都满足,但反转子链表长度为 1,不会改变顺序,相当于一次遍历,性能仍为 O(n)。

回答前,多想一步

容易答错的地方

先反转再判断不足 k
若先反转再判断,可能对不足 k 的子链也反转,导致顺序破坏。正确做法是每次循环前先走 k 步检查是否存在足够节点,不足则直接结束。
忘记更新前驱指针
每一组反转完成后,必须把前驱 prev 移动到当前组的尾部(即原起点),否则下一组会从错误位置连接,导致链表断裂或重复。
试着用自己的话回答

面试官还会怎么问?

如果要求不足 k 个时也反转,如何修改?

需要调整边界判断:每次循环先检查剩余节点数是否小于 k,若小于则反转剩余全部(将 nextGroup 置为 null,即反转从当前起点到链表尾的所有节点);若满 k 则仍按 k 步反转。不能简单去掉检查,因为需要确定反转终点。

递归实现和迭代哪一种更安全?

迭代更不易栈溢出,空间 O(1);递归代码简洁但递归深度可能达到 n/k,在链表很长时风险高。实际应用建议迭代。

当 k 很大且链表较短时,时间复杂度会退化吗?

仍为 O(n),因为每次检查走 k 步,总步数为 n;短链表时走完一遍发现不足,依然线性。额外操作为常数,没有退化。

从一道题,走向一组知识

把知识连起来

链表栈队列

如何用迭代法反转单链表并避免断链丢失节点?

同属「链表栈队列」专题,接着看 反转链表 迭代 三指针 在具体场景中的处理方式。

链表栈队列

递归反转链表时,返回值和指针翻转分别发生在递归的哪一层?

同属「链表栈队列」专题,接着看 递归 反转链表 原理 调用栈 在具体场景中的处理方式。

参考资料

  • Algorithms for Competitive Programming¶

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 分组反转的核心流程
  3. 日志序列按批倒序处理
  4. 边界判定与失败条件
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑