LeetCode 92:反转链表 II

127 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

LeetCode 92:反转链表 II

题目描述

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表

解题思路

思路一:迭代

使用LeedCode 206:反转链表 - 掘金 (juejin.cn) 的解法,

  1. 先将left 到 right 部分(待反转的区域)反转;

  2. 我们还需要记录 left 的前一个节点prev2,和 right 的后一个节点curr2, 把 prev2 的 next 指针指向反转以后的链表头节点,把反转以后的链表的尾节点的 next 指针指向 curr

如图所示:

需要注意的点:

  1. left为1,从头反转

实现代码如下:

var reverseBetween = function (head, left, right) {
  let prev = null,
    curr = head,
    next = null;
  for (let i = 1; i < left; i++) {
    prev = curr;
    curr = curr.next;
  }
  let prev2 = prev,
    curr2 = curr;
  // 反转left、right之间的链表
  for (let i = left; i <= right; i++) {
    next = curr.next;
    curr.next = prev;
    prev = curr;
    curr = next;
    // ES6的解构
    // [prev.next, prev, curr] = [prev, curr, curr.next]
  }
  // 将反转后的链表与原链表拼接
  // 将 pre2 的 next 指针指向反转后的链表头节点
  // 考虑left == 1的情况
  if (prev2 != null) {
    prev2.next = prev;
  } else {
    head = prev;
  }
  // 将反转后的链表的尾节点的 next 指针指向 curr
  curr2.next = curr;
  return head;
};

时间复杂度: O(n)  ,其中 n 是链表总节点数。最坏情况下,需要遍历整个链表2次

空间复杂度: O(1);

思路二:一次遍历,头插法

先来看看方法一的缺点:如果 left 和 right 恰好是链表的头节点和尾节点时,找到 left 和 right 需要遍历一次,反转它们之间的链表还需要遍历一次,虽然总的时间复杂度为 O(n),但遍历了链表 2 次,下一步优化为只遍历1次。
额外使用一个dummy node(哑节点)指向链表的头节点。在需要反转的区间里,每遍历到一个节点,让这个新节点来到反转部分的起始位置。 步骤:

先将 curr 的下一个节点记录为 next
curr 的下一个节点指向 next 的下一个节点;
next 的下一个节点指向 pre 的下一个节点;
pre 的下一个节点指向 next
其中curr指向待反转区域的第一个节点 left

解题代码如下:

var reverseBetween = function(head, left, right) {
    let dummy = new ListNode(-1); 
    dummy.next = head;
    let pre = dummy;
    for (let i = 0; i < left - 1; ++i) {
        pre = pre.next;
    }

    let curr = pre.next, next = null;
    for (let i = 0; i < right - left; ++i) {
        next = curr.next;
        curr.next = next.next;
        next.next = pre.next;
        pre.next = next;
    }
    return dummy.next;
};

时间复杂度: O(n)  ,其中 n 是链表总节点数。最多只遍历了链表一次

空间复杂度: O(1);

参考资料

反转链表 II - 反转链表 II - 力扣(LeetCode)