92. 反转链表II

迭代

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {

        if (left == right){
            return head;
        }

        ListNode prev = null;
        ListNode cur = head;

        /**
         * 先找到left的位置
         */
        for (int i = 1; i < left; i++) {

            ListNode next = cur.next;
            prev = cur;
            cur = next;
        }

        /**
         * 记录下left左边的第一个节点和right右边的第一个节点,为了最后还原链表
         * 循环次数n等于节点数
         */
        int n = right - left + 1;
        ListNode lastNode = null;
        ListNode firstNode = null;
        ListNode replaceFirst = null;

        while (n > 0){

            /**
             * 当left == 1时,表明头节点最后会改变,而firstNode会一直等于null
             * 此时需要单独记录下第一个节点,为了最后将其链接上原链表
             */
            if (n == right - left + 1){

                if (prev != null) {
                    firstNode = prev;
                }
                else {
                    replaceFirst = cur;
                }
            }

            if (n == 1){
                lastNode = cur.next;
            }

            /**
             * 对区间内的链表进行反转
             */
            ListNode next = cur.next;
            cur.next = prev;
            prev = cur;
            cur = next;

            n--;
        }

        /**
         * 与原链表相链接
         * 如果firstNode不存在,说明原区间最后一个节点是新的头节点,此时不能用firstNode.next,而要用之前备用的replaceFirst
         */
        if (firstNode != null) {

            firstNode.next.next = lastNode;
            firstNode.next = prev;
        }
        else {

            head = prev;
            replaceFirst.next = lastNode;
        }

        return head;
    }
}

/**
 * 时间复杂度 O(n)
 * 空间复杂度 O(1)
 */

优化1——简化判断流程(两次遍历)

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {

        /**
         * 涉及到链表问题,一般头节点会变化,因此都采用虚拟头节点,且赋值一下以免定义为空左指针
         */
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;

        /**
         * 先找到left节点
         */
        ListNode prev = dummyHead;

        for (int i = 1; i < left; i++) {
            prev = prev.next;
        }

        /**
         * 从left开始,遍历链表找到right节点
         */
        ListNode rightNode = prev.next;

        for (int i = 0; i < right - left; i++) {
            rightNode= rightNode.next;
        }

        /**
         * 分别记录left和right节点以及他们的前节点
         */
        ListNode leftNode = prev.next;
        ListNode cur = rightNode.next;

        /**
         * 将[left, right]区间独立出来进行链表反转
         */
        prev.next = null;
        rightNode.next = null;

        reverse(leftNode);

        /**
         * 和原链表接上
         * right是新的头节点,left是尾节点
         */
        prev.next = rightNode;
        leftNode.next = cur;

        return dummyHead.next;
    }

    /**
     * 反转链表
     * 不用返回头节点,因为之前保存了
     */
    public void reverse(ListNode head){

        ListNode prev = null;
        ListNode cur = head;

        while (cur != null){

            ListNode next = cur.next;
            cur.next = prev;
            prev = cur;
            cur = next;
        }
    }
}

/**
 * 时间复杂度 O(n)
 * 空间复杂度 O(1)
 */

优化2——一次遍历

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {

        /**
         * 一次遍历,在遍历[left, right]区间时就让其反转,始终让当前节点成为新的区间头节点,这样就完成了反转
         * 涉及到链表问题,一般头节点会变化,因此都采用虚拟头节点,且赋值一下以免定义为空左指针
         */
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;

        /**
         * 先找到left节点
         */
        ListNode prev = dummyHead;

        for (int i = 1; i < left; i++) {
            prev = prev.next;
        }

        /**
         * 从left开始,遍历[left, right]区间
         * 将每一个遍历到的节点变成区间的头节点
         */
        ListNode cur = prev.next;
        ListNode next;

        /**
         * prev始终指向left节点的前一个节点,这样的话其指向谁谁就是区间的头节点
         * 遍历到某个节点时,先记录下其下一个节点为next,然后断开链接,指向next的下一个节点,而next指向当前的区间头节点就是prev.next
         * 最后prev指向next,next成为新的区间头节点
         */
        for (int i = 0; i < right - left; i++) {

            next = cur.next;
            cur.next = next.next;
            next.next = prev.next;
            prev.next = next;
        }
        
        return dummyHead.next;
    }
}

/**
 * 时间复杂度 O(n)
 * 空间复杂度 O(1)
 */

https://leetcode-cn.com/problems/reverse-linked-list-ii/

posted @ 2021-12-09 14:28  振袖秋枫问红叶  阅读(47)  评论(0)    收藏  举报