链表分组反转的智慧:力扣2074题的优雅解法全解析
一、问题描述
给定一个单链表的头节点head,我们需要:
将链表节点分成若干组
第i组的长度为i(即第1组1个节点,第2组2个节点,依此类推)
反转所有偶数长度组中的节点顺序
最后返回修改后的链表
示例:
输入:head = [5,2,6,3,9,1,7,3,8,4]
输出:[5,6,2,3,9,1,4,8,3,7]
二、解题思路
采用"分组处理+条件反转"策略:
遍历链表时动态确定当前组长度
记录每组起始和结束位置
对偶数长度组执行反转操作
处理边界条件(剩余节点不足时)
三、完整代码
class Solution { public: ListNode* reverseEvenLengthGroups(ListNode* head) { ListNode dummy(0, head); ListNode* prev = &dummy; // 前一组的尾节点 int groupNum = 1; // 当前组号(决定组长度) while (prev->next) { ListNode* groupStart = prev->next; ListNode* groupEnd = groupStart; int count = 1; // 确定当前组实际长度 while (count < groupNum && groupEnd->next) { groupEnd = groupEnd->next; count++; } ListNode* nextGroup = groupEnd->next; if (count % 2 == 0) { // 反转偶数长度组 groupEnd->next = nullptr; // 断开连接 prev->next = reverseList(groupStart); groupStart->next = nextGroup; prev = groupStart; } else { // 奇数长度组不处理 prev = groupEnd; } groupNum++; // 准备处理下一组 } return dummy.next; } private: ListNode* reverseList(ListNode* head) { ListNode* prev = nullptr; ListNode* curr = head; while (curr) { ListNode* next = curr->next; curr->next = prev; prev = curr; curr = next; } return prev; } };
四、算法详解
1. 数据结构准备
2. 核心处理流程
确定组边界:遍历找出当前组的起始(start)和结束(end)节点
长度检查:统计实际节点数,处理剩余节点不足的情况
条件反转:仅当实际长度为偶数时才执行反转
重新连接:将反转后的子链表重新接入主链表
3. 反转子链表
标准的三指针反转法(prev, curr, next)
注意要先断开子链表与原链表的连接
反转后要正确设置新的连接点
五、关键点解析
1. 边界处理技巧
使用dummy节点避免头节点特殊处理
每次循环后prev指针的正确更新
剩余节点不足时按实际长度处理
2. 反转条件控制
仅当count % 2 == 0时才执行反转
反转前后要正确处理链表连接
奇数长度组只需移动prev指针
3. 复杂度控制
每个节点只被访问常数次
反转操作是原地进行的
不需要额外空间
六、常见错误分析
组长度计算错误:忘记处理剩余节点不足的情况
反转后连接错误:没有正确设置groupStart->next
指针更新错误:prev指针没有根据是否反转来正确更新
边界条件遗漏:空链表或单节点链表的处理
原创内容 转载请注明出处