leetcode
leetcode 51 ~ 100
反转链表 II

反转链表 II

难度:

标签:

题目描述

给你单链表的头指针 head 和两个整数 leftright ,其中 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

 

进阶: 你可以使用一趟扫描完成反转吗?

代码结果

运行时间: 28 ms, 内存: 15.1 MB


/*
 * 思路:
 * 1. 使用流来遍历链表。
 * 2. 在链表的第left到right之间反转子链表。
 * 3. 将反转后的链表重新链接。
 */
 
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
class ListNode {
    int val;
    ListNode next;
    ListNode(int val) { this.val = val; }
}
 
public class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        if (head == null) return null;
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy;
        for (int i = 0; i < left - 1; i++) {
            pre = pre.next;
        }
        ListNode start = pre.next;
        ListNode then = start.next;
        for (int i = 0; i < right - left; i++) {
            start.next = then.next;
            then.next = pre.next;
            pre.next = then;
            then = start.next;
        }
        return dummy.next;
    }
}

解释

方法:

题解的思路是先找到需要反转的子链表的起始节点和结束节点,然后将这一段子链表反转,最后将反转后的子链表重新连接到原链表中。具体步骤如下: 1. 创建一个虚拟头节点 dummy,指向原链表的头节点。 2. 定义两个指针 pre 和 a,初始时都指向 dummy。 3. 先将 pre 和 a 向右移动 left-1 次,使得 a 指向需要反转的子链表的前一个节点。 4. 定义指针 b,初始时指向 a,然后将 b 向右移动 right-left+1 次,使得 b 指向需要反转的子链表的后一个节点。 5. 调用 reverse 函数反转 a 和 b 之间的子链表。 6. 将反转后的子链表重新连接到原链表中,即将 pre.next 指向反转后的子链表头节点,将 a.next 指向 b。 7. 返回 dummy.next,即反转后的链表的头节点。

时间复杂度:

O(right)

空间复杂度:

O(1)

代码细节讲解

🦆
在函数reverse中,b指针是否包含在需要反转的链表部分内,还是它指向反转部分之后的第一个节点?
在函数reverse中,b指针指向需要反转的链表部分之后的第一个节点。在反转过程中,不包括b指针所指的节点,而是将a到b(不包含b)之间的节点进行反转。这意味着b是反转区间的边界外的第一个节点。
🦆
在遍历过程中移动指针pre和a,如果left为1,即从头节点开始反转,这种情况下pre和a的初始位置是否有影响?
当left为1时,意味着反转从头节点开始。在这种情况下,pre和a初始位置仍然从虚拟头节点dummy开始。由于left-1为0,因此pre和a不会移动,pre会停留在dummy上,而a会指向头节点。这保证了即使从头节点开始反转,链表结构也能正确处理,且连续性不受影响。
🦆
函数reverse执行完成后,为什么a.next直接连接到b,而没有检查b是否为None,特别是当right等于链表长度时?
在reverse函数执行后,a.next直接连接到b的原因是因为b指针在移动过程中已经考虑了其可能到达链表尾部之后的None位置。当right等于链表长度时,b会移动到链表尾部的next,即None。因此,a.next连接到b(无论是某个具体节点还是None)是安全的,这确保了链表在反转部分后能正确地接上剩余部分或正常结束。
🦆
你是如何确保在反转链表的过程中不会丢失任何节点?具体是哪些操作保证了节点的完整性?
在反转链表的过程中,确保不会丢失任何节点的关键在于使用pre和cur两个指针来重新设置节点的next指针,从而改变节点间的连接方向。在每次循环中,我们将当前节点cur的next指针指向前一个节点pre,然后更新pre和cur的位置。这种方式不仅改变了节点间的指向,还保持了遍历过程中所有节点的连续性。此外,最终将反转区域的前一节点(pre节点)的next指向反转后的头节点,以及反转区域的原始头节点(反转后的尾节点)的next指向b,确保了链表的完整性。

相关问题

反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

 

示例 1:

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

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

 

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

 

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?