题目描述
反转从位置m
到 n
的链表。请使用一趟扫描完成反转。
说明:
m,n
在区间[1, L]
之间,其中L
为链表节点总个数; 且有m <= n
示例:
输入: head = [1, 2, 3, 4, 5], m = 2, n = 4
输出: head = [1, 4, 3, 2, 5]
思路分析
这道题目是Leetcode206 题目的“加强版”,这里我们不使用Leetcode206 单个节点三个指针 遍历式的操作,因为这类解答最后还需要进行非空判断,以及非反转部分链表的指针再次更正。
我们将需要改变指针的节点插入某位置进行思考。
图1
如图1所示,我们以head = [1, 4, 3, 2, 5]
为例,(a)
为原链表,(b)
为节点改变位置的过程。在(b)
中我们将需要改变的节点一次次的插入到节点4
之后,我们进一步将(b)
过程细化可见图2:
图2
节点2
的插入过程如图2所示,我们在图中实际操作过程中为方便理解,我们标注了辅助指针curr
, 但实际过程中仅用到了currPrev
与insertPrev
两个指针(其中currPrev
指当前活动节点curr
的前置节点;insertPrev
指插入位置的前置节点)。
核心操作代码图中也单独给出,我们进行详细讲解:
代码1:
curr= currPrev.next; //暂时保存活动节点
currPrev.next = currPrev.next.next; //更新currPrev的指针状态
curr.next = insertPrev.next; //插入动作准备:更新 活动节点的后置节点
insertPrev.next = curr; //插入到insertPrev之后,这里就全部完成了
对于节点3
的插入操作与图2完全相同,只不过插入位置的后置节点为2
,但是代码是相同的。
边界问题考虑
- 当
m = 1
时
这时,头节点为活动节点,故为统一代码运行内容,我们采用哨兵机制,即在头节点前加入一伪节点,我们命名为dummyHead
; - 当
n = L
时
这里需要注意的是,最后当我们插入位置的后置节点为null
时,代码是否依旧正常运行。这里我们查看代码1,当后置节点为null
时,并不影响代码正常运行,这里直接是tmpNode.next = null
,故此问题涵盖在规则内,没有特殊影响。
步骤罗列
- 初始化三个指针
curr
、currPrev
与insertPrev
; - 使
currPrev
指向第m
个节点的前置节点;insertPrev
指向初始链表的第n
个节点(insertPrev
的间距为n-m
个节点) - 迭代核心代码(代码1),返回哨兵节点后置节点;
解题代码
public static ListNode solution(ListNode head, int m, int n) {
if (head == null || head.next == null || m >= n) {
return head;
}
/*Step1:
init pointers and dummyHead
*/
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode currPrev = dummyHead;
ListNode insertPrev = head;
ListNode curr = null;
int gapSteps = n - m;
/*Step2:
fix the gap between currPrev and insertPrev
and
find themselves pos
*/
for(int i = 0; i < gapSteps; i++)
insertPrev = insertPrev.next;
// locate pointers
for(int i = 1; i < m; i++){
currPrev = currPrev.next;
insertPrev = insertPrev.next;
}
/*Step3:
recursively insert 'curr' node
and
return dummyHead.next
*/
while(gapSteps-- > 0){
curr = currPrev.next;
currPrev.next = currPrev.next.next;
curr.next = insertPrev.next;
insertPrev.next = curr;
}
return dummyHead.next;
}
复杂度分析
时间复杂度:我们对数据遍历了一次,时间复杂度为O(N);
空间复杂度:我们没有借助额外的容器,所以空间复杂度为常量级O(1)。
官方解法比较
这里因条件限制,我们没有在力扣官网运行(待完善)。
我们在遍历数据之后,并没有进行额外的指针更正,代码相对更简洁且更易理解。
GitHub源码
完整可运行文件请访问GitHub。