关注
文末的名片达文汐
,回复关键词“力扣源码”,即可获取完整源码!!详见:源码和核心代码的区别
题目详情
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2:
输入:head = [5], left = 1, right = 1
输出:[5]
提示:
- 链表中节点数目为
n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n
进阶: 你可以使用一趟扫描完成反转吗?
解题思路
为了高效解决链表部分反转问题,我们使用 虚拟头节点(dummy node) 简化边界处理,并通过 一次遍历 定位到反转区间的前驱节点。核心步骤如下:
- 创建虚拟头节点
避免处理left=1
时头节点变更的边界情况,创建虚拟头节点指向原链表头。 - 定位反转区间的前驱节点(pre)
从虚拟头节点开始移动left-1
步,定位到反转区间的前驱节点pre
。 - 反转区间链表(left 到 right)
- 初始化反转指针:
prev = null
,curr = pre.next
(即反转区间第一个节点)。 - 迭代
right-left+1
次,每次将当前节点curr
的next
指向prev
,并更新prev
和curr
位置。 - 反转结束后,
prev
指向反转区间的头节点(原区间尾节点),curr
指向剩余链表的头节点。
- 初始化反转指针:
- 重新连接链表
- 将反转区间的前驱节点
pre
的next
指向反转后的头节点prev
。 - 将反转区间的原头节点(现在是尾节点)的
next
指向剩余链表头节点curr
。
- 将反转区间的前驱节点
代码实现(Java版)
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
// 创建虚拟头节点简化边界处理
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 定位反转区间的前驱节点 pre
ListNode pre = dummy;
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
// 初始化反转指针
ListNode prev = null;
ListNode curr = pre.next;
// 反转区间链表
for (int i = 0; i < right - left + 1; i++) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
// 重新连接链表
pre.next.next = curr; // 原反转区间头节点(现尾节点)连接剩余链表
pre.next = prev; // 前驱节点连接反转后的头节点
return dummy.next;
}
}
代码说明
- 虚拟头节点(dummy)
- 作用:避免处理
left=1
时头节点变化的特殊情况。 - 初始化:
dummy.next = head
,返回结果时始终为dummy.next
。
- 作用:避免处理
- 定位反转前驱节点(pre)
- 通过
left-1
次循环移动pre
到反转区间的前一个节点。
- 通过
- 反转区间操作
- 初始化
prev=null
和curr=pre.next
(反转区间起点)。 - 循环
right-left+1
次,每次将当前节点curr
的next
指向prev
,实现局部反转。 - 循环结束时:
prev
指向反转后的头节点(原区间尾节点),curr
指向剩余链表的头节点。
- 初始化
- 链表重连
pre.next.next = curr
:反转区间原头节点(现为尾节点)连接剩余链表。pre.next = prev
:前驱节点连接反转后的头节点,完成整链拼接。