这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战
微软和谷歌的几个大佬组织了一个面试刷题群,可以加管理员VX:sxxzs3998(备注掘金),进群参与讨论和直播
1.题目
给你单链表的头指针 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]
2.解析
反转 left 到 right 部分以后,再拼接起来。我们还需要记录 left 的前一个节点,和 right 的后一个节点。如图所示:
算法步骤:
- 第 1 步:先将待反转的区域反转;
- 第 2 步:把 pre 的 next 指针指向反转以后的链表头节点,把反转以后的链表的尾节点的 next 指针指向 succ。
具体思路见代码注释:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
// 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
// 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
ListNode pre = dummyNode;
for(int i = 1; i < left; i++){
pre = pre.next;
}
//left 节点的前一个节点
ListNode listBegin = pre;
pre = pre.next;
//left节点
ListNode subBegin = pre;
// 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
for(int i = left; i < right; i++){
pre = pre.next;
}
//right 节点
ListNode subEnd = pre;
pre = pre.next;
//right 节点后的一个节点
ListNode listEnd = pre;
// 第 3 步:切断出一个子链表(截取链表)
listBegin.next = null;
subEnd.next = null;
// 第 4 步:反转链表的子区间
ListNode subPre = null;
ListNode cur = subBegin;
while(cur != null){
ListNode curNext = cur.next;
cur.next = subPre;
subPre = cur;
cur = curNext;
}
// 第 5 步:接回到原来的链表中
subBegin.next = listEnd;
listBegin.next = subEnd;
//不能返回head,因为若输入为[3,5] left = 1, right = 2,
//head在第一个位置,反转链表后为head在第二个位置,那么返回的结果就是[3]
//返回dummyNode.next无论如何都是第一个节点,返回结果是[5,3]
return dummyNode.next;
}
}
微软和谷歌的几个大佬组织了一个面试刷题群,可以加管理员VX:sxxzs3998(备注掘金),进群参与讨论和直播