算法-数组实战【反转链表 II】中等

2 阅读2分钟

反转链表 II

给你单链表的头指针 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

 

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

以下是反转链表指定区间的 Java 实现。代码使用虚拟头节点简化边界情况处理,并通过四步定位法完成反转操作:

java
复制
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        // 创建虚拟头节点简化操作
        ListNode dummy = new ListNode(-1, head);
        ListNode prev = dummy;
        
        // Step 1: 定位 left 前一个节点
        for (int i = 0; i < left - 1; i++) {
            prev = prev.next;
        }
        
        // Step 2: 定位三个关键节点
        ListNode leftNode = prev.next;
        ListNode curr = prev.next;
        ListNode next;
        
        // Step 3: 反转区间链表
        ListNode newTail = leftNode;  // 反转后的新尾部
        ListNode preReverse = null;   // 反转前驱指针
        
        // 反转指定区间内的链表
        for (int i = 0; i < right - left + 1; i++) {
            next = curr.next;
            curr.next = preReverse;
            preReverse = curr;
            curr = next;
        }
        
        // Step 4: 重新连接链表
        prev.next = preReverse;   // 连接左段
        newTail.next = curr;      // 连接右段
        
        return dummy.next;
    }
}

算法思路说明:

  1. 虚拟头节点​:创建 dummy 节点指向头节点,处理 left=1 的反转情况

  2. 四步定位​:

    • prev:指向 left 前一个节点
    • leftNode:反转区间起始节点(反转后成为新尾部)
    • preReverse:反转区间后的新头节点
    • curr:反转区间后的下一个节点
  3. 反转过程​:

    • 遍历从 left 到 right 的节点

    • 采用标准的链表反转三指针法:

      java
      复制
      next = curr.next;   // 临时保存后续节点
      curr.next = prev;   // 反转指针方向
      prev = curr;        // 移动前驱指针
      curr = next;        // 移动当前指针
      
  4. 重新连接​:

    • 将反转后的链表头 (preReverse) 连接到左段 (prev.next)
    • 将反转后的链表尾 (newTail) 连接到右段 (curr)

复杂度分析:

  • 时间复杂度​:O(n)

    • 最坏情况需要完整遍历整个链表(当 left=1, right=n 时)
    • 实际操作只需完成单次遍历
  • 空间复杂度​:O(1)

    • 仅使用常量额外空间(几个指针变量)

示例验证:

示例 1​:head = [1,2,3,4,5], left=2, right=4

  1. 定位:prev=1, leftNode=2, newTail=2

  2. 反转区间:

    • 反转 [2,3,4] → [4,3,2]
    • preReverse=4, curr=5
  3. 连接:

    • 1 → 4(左段连接)
    • 2 → 5(右段连接)
    • 结果:1 → 4 → 3 → 2 → 5

示例 2​:head = [5], left=1, right=1

  1. 定位:prev=dummy, leftNode=5, newTail=5
  2. 反转区间:单个节点不变
  3. 连接:dummy → 5 → null
  4. 返回 [5]

此实现满足题目要求,能正确处理各种边界情况(包括从头反转、反转整个链表等特殊情况)。