算法修炼之路——【链表】Leetcode 92 反转链表 II

题目描述

反转从位置mn的链表。请使用一趟扫描完成反转。

说明:
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, 但实际过程中仅用到了currPrevinsertPrev两个指针(其中currPrev指当前活动节点curr的前置节点;insertPrev指插入位置的前置节点)。
核心操作代码图中也单独给出,我们进行详细讲解:

代码1:

curr= currPrev.next; //暂时保存活动节点
currPrev.next = currPrev.next.next; //更新currPrev的指针状态
            
curr.next = insertPrev.next; //插入动作准备:更新 活动节点的后置节点
insertPrev.next = curr; //插入到insertPrev之后,这里就全部完成了

对于节点3的插入操作与图2完全相同,只不过插入位置的后置节点为2,但是代码是相同的。

边界问题考虑

  1. m = 1
    这时,头节点为活动节点,故为统一代码运行内容,我们采用哨兵机制,即在头节点前加入一伪节点,我们命名为dummyHead;
  2. n = L
    这里需要注意的是,最后当我们插入位置的后置节点为null时,代码是否依旧正常运行。这里我们查看代码1,当后置节点为null时,并不影响代码正常运行,这里直接是tmpNode.next = null,故此问题涵盖在规则内,没有特殊影响。

步骤罗列

  1. 初始化三个指针currcurrPrevinsertPrev;
  2. 使 currPrev指向第m个节点的前置节点;insertPrev指向初始链表的第n个节点(insertPrev的间距为n-m个节点)
  3. 迭代核心代码(代码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

发布了47 篇原创文章 · 获赞 55 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/u011106767/article/details/105402543