题目
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表 ,如下图所示:
使用一次遍历完成反转。
输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5]
又如,下述示例:
输入:head = [5], left = 1, right = 1 输出:[5]
双指针法
所谓双指针法就是,一个指针用指向第一个需要反转节点的前一个节点,一个指针指向第一个要反转的节点上。
算法思路
- 定义一个指针pre指向第一个需要翻转节点的前一个节点和一个指针point指向第一个要反转的节点。如[1,2,3,4,5],left=2,right=4,pre指针就会指向值为1的节点上,point指针就会指向值为2的节点上。
- 当根据left将指针pre和指针point移动到指定位置后,开始正式的反转运算。
- 暂存point后一个节点定义为node。
- 将point的下一个节点指向point的下一个节点的下一个节点。
- 将node的下一个节点指向pre的下一个节点,即头插法。
- 将pre的下一个节点指向node。
- 重复步骤3-6完成left至right之间节点的反转。
算法步骤
- 经过一轮循环将指针pre和指针point移动到left-1下标的节点和left下标的节点,此时pre=1,point=2;
- 循环left至right下标的节点,开始反转运算;
- 反转运算第一步,暂存point的下一个节点定义为node;
- 反转运算第二步,将point的下一个节点指向point的下一个节点的下一个节点;
- 反转运算第三步,将暂存节点node的下一个节点指向pre的下一个节点;
- 反转运算第四部,将pre的下一个节点指向暂存节点node,至此完成第一轮反转运算;
- 重复步骤2-6直到完成所有节点的反转。
编码
/**
* 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) {
if(head == null || left == right || left > right){
return head;
}
ListNode res = new ListNode(-1);
res.next = head;
ListNode pre = res;
ListNode point = res.next;
for(int i=1; i<=left-1; i++){
pre = pre.next;
point = point.next;
}
for(int j=1; j<=right-left; j++){
// 反转left-right间节点
ListNode node = point.next;
point.next = point.next.next;
node.next = pre.next;
pre.next = node;
}
return res.next;
}
}
复杂度分析
根据以上算法思路和编码实现,时间复杂度为O(n),空间复杂度为O(1)。