【数据结构】链表02:LeetCode 92.反转链表Ⅱ

1.题目

原题链接:LeetCode 92. 反转链表Ⅱ

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
示例 1:
在这里插入图片描述

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

示例 2:

输入:head = [5], left = 1, right = 1
输出:[5]

提示:
链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n

2.分析与题解

思路一:先切断再拼接

思路很简单:先遍历一次链表,标记相关的节点,再把需要反转的链表部分“切”出来,将该部分反转(应用反转整个链表的方法),然后再接上。
本思路最多需要遍历两次链表。

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        //dummy节点,减少分类讨论
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode prev = dummy;
        
        //第一步:从虚拟头节点走 left - 1 步,循环结束后prev是left节点的前一个节点
        for(int i=0; i<left-1; i++){
            prev = prev.next;
        }
        
        //第二步:从left的前一个节点,再走 right - left + 1 步,循环结束后rightNode是right节点
        ListNode rightNode = prev;
        for(int i=0; i<right - left + 1; i++){
            rightNode = rightNode.next;
        }
        
        //第三步:截取并切断链表
        ListNode leftNode = prev.next;//标记left节点
        ListNode curr = rightNode.next;//标记right节点的下一个节点
        prev.next = null;//切断链表
        rightNode.next = null;//切断链表
        
        //第四步:反转链表区间并接回
        prev.next = reverseList(leftNode);
        leftNode.next = curr;
        return dummy.next;
    }
    ListNode reverseList(ListNode head){
        ListNode prev = null, curr = head, next;
        while(curr!=null){
            next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
}

时间复杂度:O(N)
空间复杂度:O(1)

思路二:头插法

前两个思路都是迭代,时间复杂度都是O(n)。但头插法只需要一次遍历,效率更高,而且也更加清晰。
下图展示了头插法的过程,来自力扣官方题解
在这里插入图片描述
“头插”核心代码:

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

全部代码:

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode prev = dummy;
        for(int i=0; i<left-1; i++){
            prev = prev.next;
        }
        ListNode curr = prev.next;
        for(int i=0; i<right - left; i++){
            ListNode next = curr.next;
            curr.next = next.next;
            next.next = prev.next;
            prev.next = next;
        }
        return dummy.next;
    }
}

思路三:递归

首先考虑一个问题:如何反转链表的前n个节点呢?
示例3:

输入:head = [1,2,3,4,5], n = 3
输出:[3,2,1,4,5]

示例4:

输入:head = [5], n = 1
输出:[5]

用迭代自然不难,但现在是递归思路,要体现递归的思想。怎么做呢?其实和递归反转全部链表是一样的:1.base case,2.调用自身,3.修改next指向关系。
base case:提示里已经说了,链表节点数不为零。所以base case就是n == 1的时候,这时不需要反转,直接返回就行。
调用自身ListNode newHead = reverseN(head.next, n-1);,直接转化为把第二个节点作为首节点的反转问题。
修改next指向关系:还是第二个节点和首节点的next指针。第二个节点指向首节点。首节点指向n+1个节点(或null),在base case那里标记一下就行了。

    ListNode mark = null;
    public ListNode reverseN(ListNode head, int n){
        if(n==1){
            mark = head.next;
            return head;//n==1是base case,不用反转,直接返回
        }
        ListNode newHead = reverseN(head.next, n-1);
        head.next.next = head;
        head.next = mark;
        return newHead;
    }

下面是示意图:
在这里插入图片描述


现在“反转链表的前n个节点”解决了,而这个问题,就是原问题的base case,也就是left == 1的情况。
然后递归调用自身:reverseBetween(head.next, left-1, right-1),再把指针改一下就行了,代码如下:

//Java
//递归
class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        if(left==1) return reverseN(head, right);
        
        head.next = reverseBetween(head.next, left-1, right-1);
        return head; 
    }
    
    ListNode mark = null;
    public ListNode reverseN(ListNode head, int n){
        if(n==1){
            mark = head.next;
            return head;//n==1是base case,不用反转,直接返回
        }
        ListNode newHead = reverseN(head.next, n-1);
        head.next.next = head;
        head.next = mark;
        return newHead;
    }
}

下面是示意图:
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值