问题
LettCode92:给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
链接: 题目
例如: 输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
例如: 输入:head = [9,7,2,5,4,3,6], left = 3, right = 6
输出:[9,7,3,4,5,2,6]
题目解读与解题思路:
题目解读:将左右指针里的元素进行反转。
解题思路:
1.头结点法。
2.穿针引线法
1.头结点法
反转的整体思想:在需要反转的区间里,每遍历到一个结点,让这个新结点来到反转部分的起始位置。
缺点:若left和right差值过大,并且恰好是链表的头结点和尾结点时,找到left和right需要遍历一次,反转它们之间的链表还需要在遍历一次。时间复杂度为O(N),但遍历了链表2次,那么如何只能遍历一次?
如图所示:
这个过程就是前面的带虚拟结点的插入操作,每走一步都要考虑各种指针怎么指,既要将结点摘下来接到对应的位置上,还要保证后续结点能够找到。
public static ListNode reverseBetween(ListNode head,int left,int right){
//虚拟头结点
ListNode dumy = new ListNode(-1);
dumy.next = head;
ListNode prev = dumy;
for (int i = 0; i < left - 1; i++) {
prev = prev.next;
}
//left 位置
ListNode cur = prev.next;
ListNode next;
for (int i = 0; i < right - left; i++) {
next = cur.next;
cur.next = next.next;
next.next = prev.next;
prev.next = next;
}
return dumy.next;
}
2.穿针引线法
如下图:反转蓝色区域的链表为例
思路:
1.先确定好需要反转的部分,即下图的left到right之间。
2.再将三段链表拼接起来,如下图。
步骤:
1.先将待反转的区域反转;
2.把pre的next指针指向反转以后的链表头节点,把反转以后的链表的尾节点的next指针指向succ。
public static ListNode reverseBetween(ListNode head, int left, int right) {
//虚拟头结点
ListNode dumy = new ListNode(-1);
dumy.next = head;
ListNode pre = dumy;
//1.从虚拟头结点 走 left -1 步,来到left 节点的前一个节点
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
//2.从pre 再走 right - left +1 ,来到 right 节点
ListNode rightNode = pre;
for (int i = 0; i < right - left; i++) {
rightNode = rightNode.next;
}
//3.切出一个子链表
ListNode leftNode = pre.next;
ListNode succ = rightNode.next;
pre.next = null;
rightNode.next = null;
//4.反转链表的子区间
reverseLinkedList(leftNode);
//5.接回到原来的链表中
pre.next = rightNode;
leftNode.next = succ;
return dumy.next;
}
private void reverseLinkedList(ListNode head) {
//也可以使用递归反转一个链表
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
}
该处使用的url网络请求的数据。