将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(n)O(n),空间复杂度 O(1)O(1)。
public ListNode reverseBetween (ListNode head, int m, int n) {
// write code here
//处理特殊情况
if(head==null||head.next==null||m==n){
return head;
}
//创建哨兵节点方便操作
ListNode dummy=new ListNode(0);
dummy.next=head;
//前驱
ListNode prev=dummy;
//移动前驱prev到m-1的位置
for(int i=1;i<m;i++){
prev=prev.next;
}
//创建当前节点cur和待处理节点curNext
ListNode cur=prev.next;
ListNode curNext=null;
//实现m~n区间的反转
for(int i=m;i<n;i++){
curNext=cur.next;
cur.next=curNext.next;
curNext.next=prev.next;
prev.next=curNext;
}
return dummy.next;//返回头节点
}
函数签名
| public ListNode reverseBetween(ListNode head, int m, int n) |
这个函数接受一个链表的头节点 head
和两个整数 m
、n
作为参数,其中 m
和 n
表示需要反转的链表区间的起始和结束位置(位置从 1 开始计数)。函数返回反转后链表的头节点。
特殊情况处理
| if(head==null||head.next==null||m==n){ |
| return head; |
| } |
- 如果链表为空(
head == null
),则无需反转,直接返回 null
(但这里实际上返回了 head
,即 null
,因为 head
已经是 null
)。 - 如果链表只有一个节点(
head.next == null
),则不存在需要反转的区间,直接返回 head
。 - 如果
m
等于 n
,表示没有需要反转的区间,也直接返回 head
。
创建哨兵节点
| ListNode dummy=new ListNode(0); |
| dummy.next=head; |
- 创建一个哨兵节点(dummy node)
dummy
,其 next
指向链表的头节点 head
。哨兵节点用于处理当 m
为 1 时(即头节点需要被反转)的特殊情况,确保 prev
节点不会变成 null
。
移动前驱节点
| ListNode prev=dummy; |
| for(int i=1;i<m;i++){ |
| prev=prev.next; |
| } |
- 初始化一个前驱节点
prev
,并将其指向哨兵节点 dummy
。 - 通过循环将
prev
移动到第 m-1
个节点的位置。注意,这里的循环是从 1 开始,直到 i
小于 m
。
反转区间
| ListNode cur=prev.next; |
| ListNode curNext=null; |
| for(int i=m;i<n;i++){ |
| curNext=cur.next; |
| cur.next=curNext.next; |
| curNext.next=prev.next; |
| prev.next=curNext; |
| } |
- 初始化当前节点
cur
为 prev.next
,即第 m
个节点。 - 在每次循环中,首先保存
cur
的下一个节点到 curNext
。 - 然后,将
cur
的 next
指针指向 curNext
的下一个节点,即跳过 curNext
。 - 接着,将
curNext
的 next
指针指向 prev.next
,即将 curNext
插入到 prev
和 cur
之间。 - 最后,更新
prev.next
为 curNext
,完成一次反转操作。 - 注意,这里的循环条件是
i < n
而不是 i <= n
,因为我们只需要反转到 n-1
的节点,而 n
位置的节点应该保持不变。
返回结果
- 由于可能存在头节点被反转的情况,我们返回哨兵节点的
next
属性,它始终指向反转后链表的真正头节点(如果头节点没有被反转,则仍然是原始的 head
)。
注意事项
- 这个函数假设输入的
m
和 n
是有效的,即它们位于链表的长度范围内。 - 链表节点的值没有在这个函数中被修改,只有节点的连接关系被改变了。
- 如果
m
为 1,则头节点可能会被反转,因此使用哨兵节点来确保操作的正确性。