【路飞 - 算法体能训练】链表 - 之反转链表II

158 阅读1分钟

每日3天分钟,快速学算法

算法体能训练计划 - 第一周

9d5cef69ly1fy8mpo6qvtg208w06oq8x.gif

题目地址 - 92. 反转链表II

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

image.png

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

注意: 我们一般在做操作链表的题目时只修改指针不修改值

思路: 利用【路飞 - 算法体能训练】链表 - 之反转链表提到的翻转方法(迭代+递归)

  • 递归思路 - 图形步骤参考上章

image.png

const reverseBetween = function(head, left, right) {
  // 通过示例和提示范围得知翻转次数为right减left加1(提示left和right都小于链表长度且大于1)
  const time = right - left + 1
  // 头节点可以会被翻转,创建个虚拟头节点方便操作头节点
  const dummy = new ListNode(null, head)
  let cur = dummy

  // 找到翻转前的头一个节点,为了方便翻转后拼接(所以这里是--left)
  while (--left) {
    cur = cur.next
  }

  // 拼接翻转后的节点
  cur.next = reverse(cur.next, time)

  return dummy.next // 返回虚拟
};

function reverse(head, n) {
  // 翻转次数为1了就不需要翻转了,返回翻转后的第一个节点
  if (!head || !head.next || n === 1) {
    return head
  }

  const result = reverse(head.next, n - 1)

  // 递归往上的过程要修改head的next指针,所以在往下的过程保存head的next指针便于操作
  let temp = head.next
  head.next = head.next.next // 传递最后一个节点的next指针
  temp.next = head

  return result
}
  • 空间复杂度:O(n)

  • 时间复杂度: O(n)

  • 迭代思路

image.png

const reverseBetween = (head, left, right) => {
  const dummy = new ListNode(null, head)
  let time = right - left + 1
  let cur = dummy
  // 到达需要翻转的头节点的上个节点
  while(--left) {
    cur = cur.next
  }

  let reverseBeforeNode = cur // 保存翻转前的头节点以便于拼接
  let reverseNode = cur.next // 保存翻转后的尾节点用于拼接后面未翻转部分
  let pre = cur // 保存翻转前的节点,用于翻转

  cur = cur.next // 当前节点是需要翻转的前一个节点,需要到下一个节点开始翻转
  while(time--) {
    let temp = cur.next
    cur.next = pre
    pre = cur
    cur = temp
  }

  reverseBeforeNode.next = pre // 拼接翻转后的头节点
  reverseNode.next = cur // 翻转后的尾节点拼接剩下的未翻转的节点

  return dummy.next
} 
  • 空间复杂度:O(1)
  • 时间复杂度: O(n)