反转链表Ⅱ
· 5 min read
力扣面试经典——92题
💡 参考代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
* 反转链表中指定区间内的节点
* @param head 链表头指针
* @param left 起始位置(从1开始计数)
* @param right 结束位置(从1开始计数)
* @return 反转后的链表头指针
*
* 算法思路:
* 1. 创建虚拟头节点,简化边界处理
* 2. 找到反转区间的前一个节点
* 3. 对[left, right]区间内的节点进行反转
* 4. 连接反转后的链表段与原链表的其余部分
*
* 时间复杂度: O(n),其中n是链表长度,只需一次遍历
* 空间复杂度: O(1),只使用常数额外空间
*/
struct ListNode* reverseBetween(struct ListNode* head, int left, int right) {
// 创建虚拟头节点,简化头节点可能被修改的情况
struct ListNode dummy;
dummy.next = head;
// 找到反转区间的前一个节点
struct ListNode* prev = &dummy;
for (int i = 1; i < left; i++) {
prev = prev->next;
}
// current指向反转区间的第一个节点
struct ListNode* current = prev->next;
// 头插法反转链表区间
// 将current后面的节点依次插入到prev后面
for (int i = 0; i < right - left; i++) {
struct ListNode* next = current->next; // 保存待插入节点
current->next = next->next; // 断开待插入节点
next->next = prev->next; // 连接节点右侧
prev->next = next; // 连接节点左侧
}
// 返回新的头节点
return dummy.next;
}
📖 总结:
点击展开题目总结
🤔 反转链表 II
1️⃣ 题目核心信息
🎯 功能描述:给定单链表的头指针和两个整数 left 和 right,其中 left <= right,反转从位置 left 到位置 right 的链表节点,返回反转后的链表。
📥 输入输出:
- 输入:
head: 链表的头节点left: 反转区间的起始位置(从1开始计数)right: 反转区间的结束位置(从1开始计数)
- 输出:反转指定区间后的链表头节点
2️⃣ 实现原理
💡 核心思路:使用虚拟头节点简化边界处理,通过头插法在一次遍历中完成指定区间的链表反转。
📋 实现步骤:
- 创建虚拟头节点,指向原链表头节点,简化边界情况处理
- 找到反转区间的前一个节点 prev
- 使用头插法,将反转区间内的节点逐个插入到 prev 后面
- 返回虚拟头节点的下一个节点作为新链表头节点
3️⃣ 关键点解析
🎯 代码技巧
- 使用虚拟头节点(dummy node)统一处理头节点可能被修改的情况
- 头插法实现链表反转,避免多次遍历
- 精确控制循环次数,只对需要反转的部分进行操作
4️⃣ 使用场景
✅ 适用情况:
- 需要反转链表中某一段连续节点
- 要求一次遍历完成操作的链表问题
- 链表局部反转操作
⚠️ 前提条件:
- 链表非空
1 <= left <= right <= 链表长度
5️⃣ 复杂度分析
- ⏱️ 时间复杂度:O(n),其中 n 是链表长度,只需一次遍历
- 💾 空间复杂度:O(1),只使用常数额外空间
6️⃣ 注意事项
🚩 边界情况:
- left = 1 时,头节点会发生变化
- left = right 时,不需要反转
- 链表只有一个节点的情况
💥 易错点:
- 链表指针操作时容易出现空指针异常
- 反转区间边界处理错误
- 忘记更新连接关系导致链表断裂
7️⃣ 补充说明
以例子来逐步演示 reverseBetween 函数的执行过程。
以示例1为例:head = [1,2,3,4,5], left = 2, right = 4
初始状态
链表结构:1 -> 2 -> 3 -> 4 -> 5 -> NULL
第一步:创建虚拟头节点
dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> NULL
第二步:找到反转区间的前一个节点(prev)
循环 left-1 = 1 次,找到第1个节点(值为1)作为 prev:
dummy -> 1(prev) -> 2(current) -> 3 -> 4 -> 5 -> NULL
第三步:开始头插法反转
第一次迭代 (i=0):
- current 指向节点2
- next 指向节点3
- 将节点3插入到 prev 后面
操作前:
dummy -> 1(prev) -> 2(current) -> 3(next) -> 4 -> 5 -> NULL
操作后:
dummy -> 1(prev) -> 4 -> 3 -> 2(current) -> 5 -> NULL
第四步:完成反转
此时已完成区间 [2,4] 的反转,最终链表为:
dummy -> 1 -> 4 -> 3 -> 2 -> 5 -> NULL
返回 dummy.next,即节点1,得到结果链表:1 -> 4 -> 3 -> 2 -> 5 -> NULL
这个过程的关键在于通过头插法,在一次遍历中就完成了指定区间的节点反转,避免了多次遍历链表的开销。
头插法的通俗解释
可以把这个过程想象成整理书架:
-
prev是书架上固定不动的标记点 -
current是你要开始整理的那本书 -
每次都把
current后面那本书(next)拿出来 -
把这本书插到标记点(
prev)后面的第一位置 -
重复这个过程,直到整理完指定数量的书
这样,原本顺序排列的书就变成了倒序排列,实现了"反转"的效果。
加载评论中...
