题目描述:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例一
l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例二
输入:l1 = [], l2 = []
输出:[]
示例三
输入:l1 = [], l2 = [0]
输出:[0]
提示
- 两个链表的节点数目范围是
[0, 50] -100 <= Node.val <= 100l1和l2均按 非递减顺序 排列
思路分析
递归
什么是递归呢?函数在运行时调用自己,这个函数就叫递归函数,调用的过程叫做递归。
比如定义函数 :
如果代入
- 返回
- 调用
- 返回
- 调用
- 返回
- ......
这时程序会无休止地运行下去,直到崩溃。 所以递归函数必须要有
终止条件,否则会出错
回归本题
终止条件:两条链表分别名为 l1 和 l2,当 l1 为空或 l2 为空时结束
如何递归:如果 l1 的 val 值更小,则将 l1.next 与排序好的链表头相接,l2同理
复杂度分析
如何计算递归的时间复杂度和空间复杂度呢?
力扣对此进行了 详细介绍 ,其中时间复杂度可以这样计算:
给出一个递归算法,其时间复杂度 通常是递归调用的数量(记作 ) 和计算的时间复杂度的乘积(表示为 )的乘积:
时间复杂度:
m,n 为 l1 和 l2的元素个数。递归函数每次去掉一个元素,直到两个链表都为空,因此需要调用 次。而在递归函数中我们只进行了 next指针的赋值操作,复杂度为 ,故递归的总时间复杂度为
空间复杂度:
对于递归调用 self.mergeTwoLists(),当它遇到终止条件准备回溯时,已经递归调用了 m+n次,使用了 m+n 个栈帧,故最后的空间复杂度为
AC代码
class Solution {
fun mergeTwoLists(l1: ListNode?, l2: ListNode?): ListNode? {
// 两条链表分别名为 l1 和 l2,当 l1 为空或 l2 为空时结束
if (l1 == null) return l2
if (l2 == null) return l1
// 如果 l1 的 val 值更小,则将 l1.next 与排序好的链表头相接,l2 同理
if (l1.`val` < l2.`val`) {
l1.next = mergeTwoLists(l1.next, l2)
// 每一层调用都返回排序好的链表头
return l1
} else {
l2.next = mergeTwoLists(l1, l2.next)
return l2
}
}
}
参考
总结
递归解法总是给人一种 只可意会不可言传 的感觉,代码一看就懂,自己动手一写就呆住了,很难受。究其原因,一是我们练习不够,二是理解不够。
顺便附一个大佬的评论
其实递归就是程序内部维护了一个栈。这个题就是每次都把最小值压入栈,最后出栈的时候,将所有数连在一起就可以了。说白了,就是用一个栈维护了顺序。最后的连接,当然是小的连小的,所以l1 小,就连到 l1,l2 小就连到 l2,最后先返回的,就是最小的头结点。
再接再厉。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情