【LeetCode】92. 反转链表 II

leetcode

 

一、解题思路

  1. ​​虚拟头节点​​:创建 dummy 节点指向原链表头节点,统一处理 left=1 的边界情况。
  2. ​​定位前驱节点​​:通过指针 pre 移动 left-1 次找到反转区间的前驱节点。
  3. ​​头插法反转​​:将反转区间的每个后续节点依次插入到 pre 之后,实现原地反转。
  4. ​​连接剩余节点​​:反转完成后自动保持与原链表后续节点的连接。

二、代码实现

type ListNode struct {
    Val  int
    Next *ListNode
}

func reverseBetween(head *ListNode, left int, right int) *ListNode {
    if head == nil || left == right {
        return head
    }

    // 创建虚拟头节点简化边界处理(如left=1时)
    dummy := &ListNode{Next: head}
    pre := dummy

    // 定位到left前驱节点(移动left-1次)
    for i := 0; i < left-1; i++ {
        pre = pre.Next
    }

    // 头插法翻转区间(right-left次循环)
    cur := pre.Next
    for i := 0; i < right-left; i++ {
        next := cur.Next     // 1.保存当前节点的
        cur.Next = next.Next // 2.当前节点指向后续未处理节点
        next.Next = pre.Next // 3.将next插入到pre之后
        pre.Next = next      // 4.更新pre的指向
    }

    return dummy.Next
}

三、复杂度分析

指标说明
时间复杂度 O(n):最多遍历链表两次(定位前驱节点+反转操作)
空间复杂度 O(1):仅使用固定数量指针变量

四、运行示例与注释说明

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

​​代码执行过程​​:

  1. pre 定位到节点1(left=2的前驱)
  2. 反转区间的3次循环操作:
    • ​​第1次​​:将节点3插入到pre后 → 链表变为 1→3→2→4→5
    • ​​第2次​​:将节点4插入到pre后 → 链表变为 1→4→3→2→5
  3. 最终输出 [1,4,3,2,5]

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

  • 直接返回原链表 [5]
func printList(head *ListNode) {
    for head != nil {
        fmt.Printf("%d->", head.Val)
        head = head.Next
    }
    fmt.Println("->nil")
}

func main() {
    // 示例1:head = [1,2,3,4,5], left = 2, right = 4
    n1 := &ListNode{1, &ListNode{2, &ListNode{3, &ListNode{4, &ListNode{5, nil}}}}}
    printList(reverseBetween(n1, 2, 4)) // 1->4->3->2->5->nil

    // 示例2:head = [5], left = 1, right = 1
    n2 := &ListNode{5, nil}
    printList(reverseBetween(n2, 1, 1)) // 5->nil
}

五、边界条件处理

场景处理方式
链表为空或单个节点 直接返回原链表(代码第2行判断)
left=right 无需反转,直接返回
反转区间包含头节点 虚拟头节点统一处理(无需特殊逻辑)

六、算法优势

  1. ​​原地操作​​:无需额外空间,仅通过指针调整实现反转
  2. ​​逻辑简洁​​:头插法将复杂指针操作简化为4步循环
  3. ​​统一处理​​:虚拟头节点规避了头节点特殊情况的复杂判断
posted @ 2025-04-19 22:07  云隙之间  阅读(36)  评论(0)    收藏  举报