链表算法面试题---反转链表 II

题目描述

完成指定区间内的链表反转

比如原链表为:1–>2–>3–>4–>5–>6–>null

要求把位置从2到4的链表反转。

所以反转结果为:1–>4–>3–>2–>5–>6–>null

前置知识

本题是反转整个链表的升级版,建议先了解下简单的反转整个链表的方式。

基础算法面试题—反转链表

基本思路

在有了前面反转整个链表的基础上,只需稍作修改,我们的想法是如果能把待反转的链表的截取出来,完成反转后,再拼回去,不就可以了嘛

也就是把2–>3–>4截取出来,变为如下样子:
1–>2–> 头尾断开 -->6–>null
然后反转2–>3–>4,得到:4–>3–>2,再拼接回去得到:1–>4–>3–>2–>5–>6–>null

示例代码


public class Code_Reverse_2 {
    
    
    public static void main(String[] args) {
    
    
        ListNode n = new ListNode(1);
        ListNode head = n;
        for (int i = 2; i < 6; i++) {
    
    
            ListNode c = new ListNode(i);
            n.next = c;
            n = n.next;
        }
        System.out.println("反转之前:" + head);
        ListNode r = reverseBetween(head, 2, 4);
        System.out.println("反转滞后:" + r);
    }

    public static ListNode reverseBetween(ListNode head, int left, int right) {
    
    
        //设置一个哑节点,以免头节点发生变化,本例中,头节点未发生变化,所以实际上也可以不用哑节点
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;

        ListNode leftPre = dummyNode;

        //leftPre来到left的前一个位置
        for (int i = 0; i < left - 1; i++) {
    
    
            leftPre = leftPre.next;
        }

        ListNode rightNode = leftPre;
        //rightNode来到right当前位置
        for (int i = 0; i < right - left + 1; i++) {
    
    
            rightNode = rightNode.next;
        }

        //left位置当前的节点
        ListNode leftNode = leftPre.next;
        //right位置的下一个节点
        ListNode rightNext = rightNode.next;

        //头断开链表
        leftPre.next = null;
        //尾断开链表
        rightNode.next = null;

        //反转链表
        reverseList(leftNode);

        //头连上链表
        leftPre.next = rightNode;
        //尾连上链表
        leftNode.next = rightNext;
        return dummyNode.next;
    }

    /**
     * 反转链表
     * @param head
     * @return
     */
    private static ListNode reverseList(ListNode head) {
    
    
        ListNode pre = null;
        ListNode next;
        while (head != null) {
    
    
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }
}

在这里插入图片描述

代码图解

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
这种方式如果反转链表的区间的范围很大,比如就是一头一尾,那么就意味着需要完整遍历一次链表,接着在反转时又需要遍历一次,那么有没有只遍历一次就完成反转的方式呢?答案是有的。

只一次遍历的方式

示例代码

    public static ListNode reverseBetween(ListNode head, int left, int right) {
    
    
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;

        ListNode leftPre = dummyNode;

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

        //cur节点初始化指向leftPre的下一个节点(在之后的反转过程中不会变)
        ListNode cur = leftPre.next;
        //始终指向cur的下一节点
        ListNode next;

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

        return dummyNode.next;
    }

代码图解:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

用红线标记当前链表节点之间的关系,可以看出节点3已经来到了节点2的前面(头插法)

在这里插入图片描述

“拉直” 了看

在这里插入图片描述
第二次循环开始,next移动一位,cur不变

在这里插入图片描述

第二次循环完成后,当前链表节点之间的关系,节点4也来到了节点3的前面(头插法)
在这里插入图片描述

最终,我们通过一次遍历完成了链表的反转。

猜你喜欢

转载自blog.csdn.net/CSDN_WYL2016/article/details/114981112
今日推荐