编程导航算法通关村第二关 | 白银:链表指定区间反转

链表指定区间反转,基础还是链表的反转。但是多了一些要考虑的情况,这里简单介绍两种方法:

1.带虚拟结点的反转

定位到链表反转区间的前一个结点,接下来遍历要反转的区间,每遍历一个结点,就把它插到反转区间的头部,如图所示:

编程导航算法通关村第二关 | 白银:链表指定区间反转_第1张图片 

上代码:

/**
     * 指定区间反转 带虚拟节点
     *
     * @param head
     */
    public static ListNode specifiedSectionReverse1(ListNode head, Integer left, Integer right) {
        //建立虚拟节点指向头部,从虚拟节点开始,这样可以避免讨论头节点也在反转区间内的情况
        ListNode virNode = new ListNode(-1);
        virNode.next = head;
        ListNode pre = virNode;
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next;
        }
        ListNode cur = pre.next;
        while (right > left) {
            ListNode next = cur.next;
            cur.next = next.next;
            next.next = pre.next;
            //把结点丢到反转区间头部
            pre.next = next;
            right--;
        }
        return virNode.next;
    }

 2.直接反转

根据反转区间,把链表分成三段:链表前面部分,要反转的部分,链表后面部分,然后调用方法把中间那段要反转的链表反转一遍,最后三段链表拼在一起:

编程导航算法通关村第二关 | 白银:链表指定区间反转_第2张图片

定位到四个位置,然后切割

编程导航算法通关村第二关 | 白银:链表指定区间反转_第3张图片

 中间的链表反转后,最后直接拼上去编程导航算法通关村第二关 | 白银:链表指定区间反转_第4张图片

代码如下:

    /**
     * 指定区间反转 不带虚拟节点
     *
     * @param head
     */
    public static ListNode specifiedSectionReverse2(ListNode head, Integer left, Integer right) {
        ListNode preNode, leftNode, rightNode, succNode;
        //动用双指针,走完一遍后,preNode是第一条链表尾节点,leftNode是中间链表开始结点,
        // rightNode是中间链表结束结点,succNode是第三条链表开始结点

        //创建虚拟节点的next指向头节点,避开头节点也在反转范围的一大堆情况讨论
        ListNode virNode = new ListNode(-1);
        virNode.next = head;
        ListNode fast = virNode;
        while (right - left + 1 > 0) {
            fast = fast.next;
            right --;
        }
        ListNode slow = virNode;

        for (int i = 0; i < left - 1; i++) {
            slow = slow.next;
            fast = fast.next;
        }
        preNode = slow;
        leftNode = slow.next;
        rightNode = fast;
        succNode = fast.next;
        //记得把中间链表最后节点指向null,否不然第三段链表与第二段链表连在一起,会在下面反转时一起反转
        rightNode.next = null;

        reverListNode(leftNode);
        preNode.next = rightNode;
        leftNode.next = succNode;

        return virNode.next;
    }

    //反转链表
    public static ListNode reverListNode(ListNode head){
        ListNode cur = head;
        ListNode pre = null;
        while (cur != null){
            ListNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }

 值得注意的是,java对象传参传的是地址,中间的链表已经被反转后,specifiedSectionReverse2函数里的 rightNode 和 leftNode 依旧是原来的对象地址,直接用 preNode 的 next 指向 leftNode,用 rightNode 的next 指向 succNode 就成功拼接在一起了

目录

1.带虚拟结点的反转

 2.暴力反转


 

你可能感兴趣的:(算法,链表,数据结构)