文章目录
LeetCode 92. 反转链表 II - 算法解析
问题描述
给你单链表的头指针 head 和两个整数 left 和 right,其中 left <= right。请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表。
代码实现
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if(head==null || head.next==null){
return head;
}
ListNode dummy = new ListNode(0,head);
ListNode preLeft = dummy;
for (int i = 0; i < left - 1; i++) {
preLeft=preLeft.next;
}
ListNode pre=null,cur=preLeft.next;
for (int i = 0; i < right - left + 1; i++) {
ListNode nxt = cur.next;
cur.next= pre;
pre=cur;
cur=nxt;
}
preLeft.next.next=cur;
preLeft.next=pre;
return dummy.next;
}
}
算法思路
这个算法采用原地反转的方式,分为以下几个关键步骤:
1. 边界处理和初始化
if(head==null || head.next==null){
return head;
}
ListNode dummy = new ListNode(0,head);
- 处理空链表或单节点链表的边界情况
- 创建虚拟头节点
dummy
,简化边界处理(特别是当left=1时)
2. 找到反转区间的前一个节点
ListNode preLeft = dummy;
for (int i = 0; i < left - 1; i++) {
preLeft=preLeft.next;
}
preLeft
指向第left
个节点的前一个节点- 这个节点很关键,因为反转后需要重新连接
3. 反转指定区间的链表
ListNode pre=null,cur=preLeft.next;
for (int i = 0; i < right - left + 1; i++) {
ListNode nxt = cur.next;
cur.next= pre;
pre=cur;
cur=nxt;
}
这是标准的链表反转操作:
pre
: 前一个节点(初始为null)cur
: 当前节点(从第left个节点开始)nxt
: 下一个节点(临时保存)
反转过程:
- 保存下一个节点:
nxt = cur.next
- 反转当前节点的指针:
cur.next = pre
- 移动指针:
pre = cur
,cur = nxt
- 重复
right - left + 1
次
4. 重新连接链表
preLeft.next.next=cur;
preLeft.next=pre;
preLeft.next.next=cur
: 将原来的第left个节点(现在是反转区间的尾节点)连接到反转区间后面的节点preLeft.next=pre
: 将preLeft连接到反转后的头节点
图解示例
以head = [1,2,3,4,5], left = 2, right = 4
为例:
初始状态
dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> null
^ ^ ^
preLeft | |
反转区间起点 反转区间终点
找到preLeft
dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> null
^
preLeft
反转过程
第1次反转:
pre=null, cur=2, nxt=3
2.next = null
pre=2, cur=3
第2次反转:
pre=2, cur=3, nxt=4
3.next = 2
pre=3, cur=4
第3次反转:
pre=3, cur=4, nxt=5
4.next = 3
pre=4, cur=5
重新连接
preLeft.next.next = cur // 2.next = 5
preLeft.next = pre // 1.next = 4
最终结果
dummy -> 1 -> 4 -> 3 -> 2 -> 5 -> null
返回[1,4,3,2,5]
复杂度分析
时间复杂度:O(n)
- 第一个循环:O(left) ≤ O(n)
- 第二个循环:O(right - left + 1) ≤ O(n)
- 总体:O(n)
空间复杂度:O(1)
- 只使用了常数个额外变量
- 原地反转,没有使用额外的数据结构