leetcode92-反转链表 II

112 阅读2分钟

题目

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例 1:

输入: head = [1,2,3,4,5], left = 2, right = 4

输出: [1,4,3,2,5]

来源:力扣(LeetCode)
链接:leetcode.cn/problems/re…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

代码

法一:两次遍历(找到各个断点,切断需要反转的部分,然后反转;再接上)

/**
     * 法一:两次遍历
     * 第一次:先找到pre 和 找到rightNode;然后把中间那块儿反转即可
     * @param head
     * @param left
     * @param right
     * @return
     */
    public ListNode reverseBetween0(ListNode head, int left, int right) {
        // 虚拟节点:很好的思想,这种头部会变得逻辑,都可以借鉴
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;
        
        // 找到pre
        ListNode pre = dummyNode;
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next;
        }

        // 找到rightNode
        ListNode rightNode = pre;
        for (int i = 0; i < right - left+1; i++) {
            rightNode = rightNode.next;
        }

        // 切断
        ListNode leftNode =  pre.next;
        pre.next = null;

        ListNode last = rightNode.next;
        rightNode.next = null;

        // 反转断掉的部分
        ListNode reverse = reverse(leftNode);

        // 重新指向
        leftNode.next = last;
        pre.next = reverse;

        return dummyNode.next;
    }

    public ListNode reverse(ListNode head) {
        if(null==head || head.next==null){
            return head;
        }
        ListNode lastNode = reverse(head.next);
        ListNode next = head.next;
        head.next = null;
        next.next = head;
        return lastNode;
    }

一次遍历:头插法

/**
     * 一次遍历:头插法
     * @param head
     * @param left
     * @param right
     * @return
     */
    public ListNode reverseBetween(ListNode head, int left, int right) {
        // 虚拟节点:很好的思想,这种头部会变得逻辑,都可以借鉴
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;

        // 这里需要让pre的默认值等于傀儡节点,为的是兼容(left==1的情况,比如[3,5] ,1 ,2)
        ListNode pre = dummyNode;
        for (int i = 0; i < left-1; i++) {
            pre = pre.next;
            head = head.next;
        }
        head = head.next;
        for (int i = 0; i < right-left; i++) {
            ListNode next = head.next;
            head.next = next.next;
            next.next = pre.next;
            pre.next = next;
        }
        return dummyNode.next;
    }

    public static class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
        }
    }

解析

法一:两次遍历, 大部分人的思维应该都是这样,但是链表的处理不合适,很容易造成环;上面代码有两个点很好的拆解了复杂度

  1. 利用傀儡节点,不用额外关心头节点变的逻辑
  2. 先找到各个连接点并记录下来,然后将需要反转的部分单独切断出来去反转,然后再拼上;逻辑、代码都会清晰很多,也不可能会成环

法二: 头插法

hashmap中运用到的经典算法,能想到用这个方法的话,代码并不难写;主要还是注意别成环;以及注意pre节点

总结

这是针对,前面纯粹反转链表的进阶;后面还有K次反转,会再仔细分析