LeetCode 25. K 个一组翻转链表 | 硬核拆解「reverse N」核心思路

LeetCode 25. K 个一组翻转链表 | 硬核拆解「reverse N」核心思路

✨ LeetCode 25. K 个一组翻转链表 | 硬核拆解「reverse N」核心思路

链表作为数据结构的基础考点,K 个一组翻转链表是进阶高频题型,它完美融合了「单组链表翻转」与「分段处理」的核心思想。本文将严格围绕reverse N 实现思路,从问题定义、算法原理、代码实现到调试踩坑,逐帧拆解这道经典题,带你彻底吃透链表分段翻转的底层逻辑!


视频地址

因为想更好的为大佬服务,制作了同步视频,这是Bilibili的视频地址

🔍 一、问题核心含义:K 个一组翻转链表

1. 官方定义(严格版)

k 个节点为一个分组,对链表的每一组进行完整反转;若链表剩余节点数量不足 k 个,则保持原有顺序不翻转

2. 直观示例

输入链表:1 -> 2 -> 3 -> 4 -> 5,k = 3

  • 第一组:1、2、3(满足 3 个节点)→ 翻转 → 3 -> 2 -> 1
  • 第二组:4、5(不足 3 个节点)→ 不翻转
  • 最终输出:3 -> 2 -> 1 -> 4 -> 5

3. 可视化流程图(Mermaid)

原链表: 1->2->3->4->5

分组 k=3

第一组: 1,2,3

第二组: 4,5

翻转后: 3->2->1

不翻转: 4->5

最终链表: 3->2->1->4->5

图表说明:清晰展示了「分组→判断长度→分组翻转→拼接结果」的全流程,不足 k 个的分组直接保留原序。


📊 二、算法原理:基于「reverse N」的分层实现

本题的核心解题思路是拆分法:将「K 组翻转」拆解为两个基础能力的组合——

  1. reverse N 计数判断:检查当前链表是否有足够的 N 个节点供翻转;
  2. __reverse N 私有翻转:真正实现「翻转链表前 N 个节点」的核心逻辑。

这是官方推荐的最优解思路,无冗余操作,时间复杂度趋近于 O(n)。

1. 基础能力:reverse N 节点计数判断

作用:前置校验,避免对不足 k 个节点的分组执行翻转。

实现逻辑:

  • 定义指针 p 指向当前分组的头节点;
  • 循环递减 k,同时让 p 向后遍历;
  • 若循环结束后 p 不为空 → 剩余节点 ≥k,可翻转;
  • p 为空 → 剩余节点 <k,终止翻转。

2. 核心能力:__reverse N 前 N 个节点翻转

这是真正执行翻转的私有函数,严格遵循题目指定逻辑:

  • 递归终止条件:n == 1,直接返回当前头节点(最后一个节点是新头);
  • 定义 tail:指向 head->next(原分组头节点,翻转后成为分组尾节点);
  • 定义 newHead:递归翻转后的新头节点;
  • 最终返回 newHead(翻转后分组的头节点)。

💡 三、整体实现框架:虚拟头节点 + 双指针循环

链表翻转的最大坑点是头节点会变化,因此必须使用虚拟头节点技巧破局;同时通过双指针记录关键位置,保证分组拼接不断裂。

1. 核心技巧:虚拟头节点

创建一个虚拟节点 dummy,让 dummy->next = 原链表头节点

作用:无论链表头节点如何翻转,最终直接返回 dummy->next 即可,彻底解决头节点边界问题

2. 关键位置指针定义

我们定义两个核心指针,这是循环翻转的关键:

指针名称核心作用位置变化
p待翻转区域的前驱节点始终指向当前分组的前一个节点,初始指向虚拟头
q待翻转区域的头节点 / 翻转后的尾节点初始 = p->next,翻转后成为当前分组的尾节点,是下一次的 p

3. 循环翻转逻辑

  1. reverseN(q, k) 为循环条件(返回非空说明可翻转);
  2. 执行翻转,将前驱节点 p 与新头节点拼接;
  3. 指针后移:p = q(当前分组尾 → 下一组前驱),q = p->next(下一组头);
  4. 直到 reverseN 返回空,循环结束。

⚠️ 四、C++ 关键代码实现

以下是精简核心版代码,仅保留性能关键逻辑,注释全覆盖核心步骤:

 #include <iostream> using namespace std; // 链表节点定义 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: // ===================== 核心1:翻转前n个节点(__reverseN)===================== ListNode* __reverseN(ListNode* head, int n) { // 终止条件:n=1,返回当前头(翻转后新头) if (n == 1) return head; // tail:原分组头,翻转后是分组尾节点 ListNode* tail = head->next; // 递归翻转剩余n-1个节点 ListNode* newHead = __reverseN(head->next, n-1); // 拼接节点:尾节点指向当前头 tail->next = head; head->next = nullptr; return newHead; } // ===================== 核心2:判断是否有n个节点(reverseN判断)===================== ListNode* hasNNode(ListNode* head, int n) { ListNode* p = head; // 向后数n个节点 while (n-- && p) { p = p->next; } // p非空=有n个节点,返回p;空=不足n个,返回nullptr return p; } // ===================== 主函数:K个一组翻转 ===================== ListNode* reverseKGroup(ListNode* head, int k) { // 虚拟头节点(解决头节点翻转边界) ListNode* dummy = new ListNode(-1); dummy->next = head; // p:待翻转区前驱;q:待翻转区头节点 ListNode* p = dummy; ListNode* q = p->next; // 循环:有k个节点则翻转,否则终止 while (hasNNode(q, k)) { // 翻转q开头的k个节点,得到新头 ListNode* newHead = __reverseN(q, k); // 前驱节点拼接新头 p->next = newHead; // 指针后移:p=当前分组尾,q=下一组头 p = q; q = p->next; } // 返回最终链表头(虚拟头的next) return dummy->next; } }; 

🐛 五、调试实战:踩坑与修复(返回值核心问题)

在编写代码时,返回值错误是最常见的bug,结合题目要求复盘调试过程:

1. 初始错误

✅ 问题:最初直接返回 head(原链表头节点),导致结果错乱;

✅ 原因:分组翻转后,原链表头节点变成了第一组的尾节点,不再是整个链表的头。

2. 修复方案

返回虚拟头节点的nextreturn dummy->next,无论头节点如何翻转,虚拟头始终指向链表真正的头。

3. 翻转判断逻辑

题目指定关键判断:

if (p->next != q) → 说明发生了翻转;

if (p->next == q) → 说明未翻转(不足k个节点)。

该判断用于验证分组是否成功翻转,是调试的核心依据。


📈 六、复杂度分析

  1. 时间复杂度:O(n)

每个节点仅被遍历一次(计数一次 + 翻转一次),无嵌套循环;

  1. 空间复杂度:O(k)

递归调用栈深度为 k(分组长度),若改用迭代版 __reverseN,空间可优化为 O(1)。


✅ 七、总结与技巧提炼

  1. 核心思想:大问题拆小 → 用 reverseN 做计数校验,__reverseN 做单组翻转;
  2. 必用技巧:虚拟头节点解决链表头变化的边界问题;
  3. 指针关键p 记录前驱,q 记录分组头尾,保证链表不断裂;
  4. 终止条件:剩余节点不足 k 个时,直接终止循环,不做任何操作。
 LeetCode 25. K 个一组翻转链表 | 硬核拆解「reverse N」核心思路

这道题是链表递归+迭代的综合运用,吃透「reverse N」思路,所有链表分段翻转类题目都能迎刃而解!

Read more

HDFS读写机制深度解析:分布式存储的核心奥秘

HDFS读写机制深度解析:分布式存储的核心奥秘

目录 * HDFS读写机制深度解析:分布式存储的核心奥秘 * 摘要 * 1. HDFS架构概览 * 1.1 核心组件解析 * 1.2 数据块管理机制 * 2. HDFS写入机制深度剖析 * 2.1 写入流程概述 * 2.2 副本放置策略 * 3. HDFS读取机制详解 * 3.1 读取流程实现 * 3.2 读取性能优化 * 4. 容错机制与数据一致性 * 4.1 故障检测与恢复 * 4.2 性能对比分析 * 5. 性能优化最佳实践 * 5.1 配置优化 * 5.2 应用层优化 * 6. 监控与运维 * 6.1 关键指标监控 * 6.

By Ne0inhk
【数据结构入坑指南(六)】--《从初始化到销毁:手把手教你打造健壮的队列实现》

【数据结构入坑指南(六)】--《从初始化到销毁:手把手教你打造健壮的队列实现》

🔥@晨非辰Tong:个人主页  👀专栏:《C语言》、《数据结构与算法》、《数据结构与算法刷题集》 💪学习阶段:C语言、数据结构与算法初学者 ⏳“人理解迭代,神理解递归。” 引言:刚征服了“后进先出”的栈,现在让我们迎接一个全新的挑战——队列。这个“先进先出”的数据结构将带你体验截然不同的设计思维。本文将手把手带你用链表实现一个队列,为后续学习树形结构打下坚实基础。 目录  一、队列初探:核心概念与结构设计 1.1  深入理解“先进先出”(FIFO) 1.1.1  关键抉择:链表 vs 数组 1.2  搭建队列的“骨架” 二、核心功能实现:从零搭建完整队列 2.1  准备工作:搭建稳固的基础 2.

By Ne0inhk
通俗易懂->哈希表详解

通俗易懂->哈希表详解

目录 一、什么是哈希表? 1.1哈希表长什么样? 1.2为什么会有哈希表? 1.3哈希表的特点 1.3.1 取余法、线性探测 1.3.2 映射 1.3.3负载因子 1.4哈希桶 1.5闲散列与开散列 1.6总结 二、设计hash表 1、哈希表的设计   1)插入   2)查找  3)删除 4)字符串哈希算法 2、封装map和set 1、完成对hash表的基础功能 2、完成封装 3、对应的迭代器 4、【】方括号重载 三、

By Ne0inhk
从零开始打造高性能数据结构——手把手教你实现环形缓冲

从零开始打造高性能数据结构——手把手教你实现环形缓冲

◆ 博主名称: 小此方-ZEEKLOG博客 大家好,欢迎来到小此方的博客。 ⭐️个人专栏:《C语言》_小此方的博客-ZEEKLOG博客 算法_小此方的博客-ZEEKLOG博客  ⭐️踏破千山志未空,拨开云雾见晴虹。 人生何必叹萧瑟,心在凌霄第一峰。 目录 一,普通队列的劣势 1. 空间浪费严重(“假溢出”问题) 2. 需要频繁移动元素(若避免浪费) 3. 扩容成本高 4. 无法解决“假溢出”导致的提前扩容 二,环形缓冲结构分析  1. “循环”取模实现指针回绕  2.“循环”,轮流入座而不是排长队 三,实现环形缓冲 1,MyCircularQueue(k): 构造器   1,结构体搭建   2,初始化 3,为什么选择k+1块空间而不是k块空间?

By Ne0inhk