合并两个有序链表

168 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

合并两个有序链表

LeetCode链接:21. 合并两个有序链表 - 力扣(LeetCode) (leetcode-cn.com)

题目描述

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

例如:

merge_ex1.jpeg

题目分析

要合并两个有序链表,那么就是从两个链表的头结点开始,比较哪个小,就把哪个加到结果里

例如,要合并 [1,2,4] 和 [1,3,4] ,则可以看作要合并 1 和 [2,4]与[1,3,4] 的合并结果,同理:

1、合并 [2,4]与[1,3,4],那就看作合并 1 和 [2,4]与[3,4] 的合并结果

2、合并[2,4]与[3,4],就看作合并 2 与 [4]与[3,4] 的合并结果

...

因此可以将这个问题转化为递归实现

1. 递归

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} list1
 * @param {ListNode} list2
 * @return {ListNode}
 */
var mergeTwoLists = function (list1, list2) {
  // 一条链表为空,则直接返回另一条链表
  if (!list1) return list2
  if (!list2) return list1
  if (list1.val < list2.val) {
    // 谁小就将谁的 next 指向剩余链表的合并结果
    list1.next = mergeTwoLists(list1.next, l2)
    // 然后返回当前链表
    return list1
  } else {
    list2.next = mergeTwoLists(list2.next, list1)
    return list2
  }
}

理解递归实现时,我们只需要将 mergeTwoLists(list1.next, l2) 当做一个已经合并完成的链表

如果 list1.val 更小,那么肯定是 list1.next 指向这个已合并完成的链表,然后返回 list1

递归操作只是在不断缩小这个已合并的链表,直到某一条链表为空,不能继续缩小时,就该返回了。

时间复杂度:O(m+n),m 和 n 分别为两个链表的长度

空间复杂度:O(m+n),因为递归函数最多调用 m+n 次

其实感觉我们常规思路一般是:遍历链表,然后比较当前两个节点哪个的值小,就把哪个节点接到头结点的后面

2. 循环遍历

首先我们先设置一个 head 节点用于保存头结点,最后好返回;然后设置一个 p 节点用于遍历链表,将较小的节点接在 p 的后面,然后 p 节点往前一步,再继续遍历

var mergeTwoLists = function (list1, list2) {
  let head = new ListNode()
  let p = head
  // 遍历两个链表,其中有一个为空即可退出循环
  while (list1 && list2) {
    if (list1.val < list2.val) {
      // 谁小就把谁接在 p 后面,然后向前一步
      p.next = list1
      list1 = list1.next
    } else {
      p.next = list2
      list2 = list2.next
    }
    p = p.next
  }
  // 最后谁还有剩余,就接在 p 的后面即可
  p.next = list1 || list2
  return head.next
}

时间复杂度:O(m+n),遍历次数不会超过两个链表长度之和

空间复杂度:O(1),只用了两个额外的变量