加载中...
21-合并两个有序链表(Merge Two Sorted Lists)
发表于:2021-12-03 | 分类: 简单
字数统计: 232 | 阅读时长: 1分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/merge-two-sorted-lists

英文原文

You are given the heads of two sorted linked lists list1 and list2.

Merge the two lists in a one sorted list. The list should be made by splicing together the nodes of the first two lists.

Return the head of the merged linked list.

 

Example 1:

Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]

Example 2:

Input: list1 = [], list2 = []
Output: []

Example 3:

Input: list1 = [], list2 = [0]
Output: [0]

 

Constraints:

  • The number of nodes in both lists is in the range [0, 50].
  • -100 <= Node.val <= 100
  • Both list1 and list2 are sorted in non-decreasing order.

中文题目

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

 

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

 

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1l2 均按 非递减顺序 排列

通过代码

高赞题解

1. 前言

递归解法总是给人一种“只可意会不可言传”的感觉,代码一看就懂,自己动手一写就呆住了,很难受。究其原因,一是我们练习不够,二是理解不够。

2. 什么是递归?

递归的例子在平时生活中很容易见到,比如:

1.png

开个玩笑😁

什么是递归呢?函数在运行时调用自己,这个函数就叫递归函数,调用的过程叫做递归。
比如定义函数 $f(x)=x+f(x-1)$:

[]
def f(x): return x + f(x-1)

如果代入 $f(2)$:

  • 返回 $2+f(1)$;
  • 调用 $f(1)$;
  • 返回 $1+f(0)$;
  • 调用 $f(0)$;
  • 返回 $0+f(-1)$
  • ……

这时程序会无休止地运行下去,直到崩溃。
如果我们加一个判断语句 x > 0

[]
def f(x): if x > 0: return x + f(x-1) else: # f(0) = 0 return 0

这次计算 $f(2)=2+f(1)=2+1+f(0)=2+1+0=3$。我们从中总结两个规律:

  • 递归函数必须要有终止条件,否则会出错;
  • 递归函数先不断调用自身,直到遇到终止条件后进行回溯,最终返回答案。

3. 递归解法

根据以上规律考虑本题目:

  • 终止条件:当两个链表都为空时,表示我们对链表已合并完成。
  • 如何递归:我们判断 l1l2 头结点哪个更小,然后较小结点的 next 指针指向其余结点的合并结果。(调用递归)

<幻灯片1.JPG,幻灯片2.JPG,幻灯片3.JPG,幻灯片4.JPG,幻灯片5.JPG,幻灯片6.JPG,幻灯片7.JPG,幻灯片8.JPG>

代码

感谢 @huwt 提供的 c++ 代码!

[]
class Solution: def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: if not l1: return l2 # 终止条件,直到两个链表都空 if not l2: return l1 if l1.val <= l2.val: # 递归调用 l1.next = self.mergeTwoLists(l1.next,l2) return l1 else: l2.next = self.mergeTwoLists(l1,l2.next) return l2
[]
class Solution { public ListNode mergeTwoLists(ListNode l1, ListNode l2) { if (l1 == null) { return l2; } else if (l2 == null) { return l1; } else if (l1.val < l2.val) { l1.next = mergeTwoLists(l1.next, l2); return l1; } else { l2.next = mergeTwoLists(l1, l2.next); return l2; } } }
[]
class Solution { public: ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { if (l1 == NULL) { return l2; } if (l2 == NULL) { return l1; } if (l1->val <= l2->val) { l1->next = mergeTwoLists(l1->next, l2); return l1; } l2->next = mergeTwoLists(l1, l2->next); return l2; } };

复杂度分析

如何计算递归的时间复杂度和空间复杂度呢? 力扣对此进行了 详细介绍 ,其中时间复杂度可以这样计算:

给出一个递归算法,其时间复杂度 ${\mathcal{O}(T)}$ 通常是递归调用的数量(记作 ${R}$) 和计算的时间复杂度的乘积(表示为 ${\mathcal{O}(s)}$)的乘积:${\mathcal{O}(T) = R * \mathcal{O}(s)}$

时间复杂度:${\mathcal{O}}(m + n)$。

$m$,$n$ 为 $l_{1}$ 和 $l_{2}$ 的元素个数。递归函数每次去掉一个元素,直到两个链表都为空,因此需要调用 $R=O(m + n)$ 次。而在递归函数中我们只进行了 next 指针的赋值操作,复杂度为 $\mathcal{O}(1)$,故递归的总时间复杂度为 ${\mathcal{O}(T) = R * \mathcal{O}(1)}={\mathcal{O}}(m + n)$ 。

空间复杂度:${\mathcal{O}}(m + n)$。**

对于递归调用 self.mergeTwoLists(),当它遇到终止条件准备回溯时,已经递归调用了 $m+n$ 次,使用了 $m+n$ 个栈帧,故最后的空间复杂度为 ${\mathcal{O}}(m + n)$。

相关题目

以下是一些基础但很经典的题目,值得我们好好练习:

欢迎提供 C++ 代码
如有问题,欢迎讨论~

统计信息

通过次数 提交次数 AC比率
782798 1173070 66.7%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言

相似题目

题目 难度
合并K个升序链表 困难
合并两个有序数组 简单
排序链表 中等
最短单词距离 II 中等
上一篇:
20-有效的括号(Valid Parentheses)
下一篇:
22-括号生成(Generate Parentheses)
本文目录
本文目录