链表

核心技巧:辅助头节点 · 快慢指针 · 画图理解指针交换

2 两数相加

辅助头节点 + 进位处理

题干:两个链表表示逆序数字,求和后返回新链表。如 (2→4→3) + (5→6→4) = 7→0→8

  • 辅助头节点 pre,最后返回 pre.next
  • l1 || l2 统一循环,空节点取 0
// 简化版:统一循环
var addTwoNumbers = function(l1, l2) {
  let p = new ListNode()
  let curP = p
  let carry = 0
  while (l1 || l2) {
    let val1 = l1?.val || 0
    let val2 = l2?.val || 0
    let tempSum = val1 + val2 + carry
    carry = 0
    if (tempSum > 9) {
      carry = 1
      tempSum -= 10
    }
    if (l1) l1 = l1.next
    if (l2) l2 = l2.next
    curP.next = new ListNode(tempSum)
    curP = curP.next
  }
  if (carry) curP.next = new ListNode(1)
  return p.next
}

19 删除链表的倒数第 N 个结点

快慢指针:先拉开 n+1 间距

题干:删除链表的倒数第 n 个节点,返回头节点。

  • 头节点可能被删 → 必须构造辅助头节点
  • p2 先走 n+1 步,然后 p1/p2 同步走,p2 到头时 p1 刚好在待删节点前一个
var removeNthFromEnd = function(head, n) {
  let pre = new ListNode()
  pre.next = head
  let p1 = pre, p2 = pre
  // 拉开 n+1 个间隔
  while (n + 1 > 0) { p2 = p2.next; n-- }
  while (p2) { p1 = p1.next; p2 = p2.next }
  p1.next = p1.next.next // 删除
  return pre.next
}

21 合并两个有序链表

辅助头节点 + 逐个比较拼接
var mergeTwoLists = function (list1, list2) {
  let pre = new ListNode()
  let cur = pre
  while (list1 && list2) {
    if (list1.val <= list2.val) {
      cur.next = list1
      list1 = list1.next
    } else {
      cur.next = list2
      list2 = list2.next
    }
    cur = cur.next
  }
  // 剩余直接接上
  if (list1) cur.next = list1
  if (list2) cur.next = list2
  return pre.next
}

24 两两交换链表中的节点

三指针 n1/n2/n3 · 画图理解

题干:两两交换相邻节点。1→2→3→42→1→4→3

  • :交换以后跳两个节点应该是 n1=n2(因为 n2 已经被交换到后面了)
var swapPairs = function(head) {
  let n1 = new ListNode()
  n1.next = head
  let helper = n1

  let n2 = n1?.next
  let n3 = n2?.next

  while (n2 && n3) {
    // 交换 n2 和 n3
    n2.next = n3.next
    n3.next = n2
    n1.next = n3

    n1 = n2 // 注意:交换后 n2 在后面,跳两个是 n1=n2
    n2 = n1.next
    n3 = n2?.next
  }
  return helper.next
}