递归版题解
这题曾经写过,原文点这里,之前对递归的图解有点简单。
这次把递归版本重新写了一下,着重于图解部分,将递归怎么一步步调用的,怎么一步步返回的详细画了一遍。
先来回顾下这道题目吧:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
递归的两个条件:
- 终止条件:当l1或者l2中有一个为空时,就返回
- 函数内要做什么:如果l1.val小于等于l2.val,那么将l1.next指向 l1的后续节点和l2中较小的一个
终止条件比较好理解,函数内做的事情就不那么好懂了 l1的后续节点和l2中较小的一个
如果是 l1.val<=l2.val,我们就能确定一个较小的节点了,也就是l1。因为链表还没结束,我们要继续比较,这时候比较的是l1.next和l2。
反之,如果是l1.val>l2.val,我们也能确定一个较小的节点,现在是l2.因为链表还没结束,我们继续比较,这时候比较的是l1和l2.next。
后面就是不断的递归,最终把整个链表给串起来。
java版本如下:
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//递归的结束条件,如果l1和l2中有一个为空就返回
if(l1==null || l2==null) {
return (l1==null)? l2:l1;
}
//如果l1的值<=l2的值,就继续递归,比较l1.next的值和l2的值
//l1.next和l2比较完后,会产生一个更小的节点x,将x加到当前l1的后面
if (l1.val<=l2.val) {
l1.next = mergeTwoLists(l1.next,l2);
return l1;
//如果l1的值>l2的值,就继续递归,比较l1的值和l2.next的值
} else {
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
}
python版本如下:
class Solution(object):
def mergeTwoLists(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
#递归的结束点就是L1或者L2为空就返回
#如果L1为空就返回L2,L2为空返回L1
ifnot (l1 and l2):
return l1 if l1 else l2
#L1的val<=L2的val,那么继续递归
#当前L1的next指向一个递归函数
#意思是L1的下一个和L2哪个大,就作为当前L1的next
if l1.val<=l2.val:
l1.next = self.mergeTwoLists(l1.next,l2)
return l1
else:
l2.next = self.mergeTwoLists(l1,l2.next)
return l2
时间复杂度:O(n + m),其中 n 和 m 分别为两个链表的长度
空间复杂度:O(n + m)
详解执行过程
我们定义两个链表:1->2->4
,1->3->4
L1即为第一个链表,L2为第二个链表
两个链表下方是代码部分,具体如下图所示:
再解释一下上图中代码部分:
- 第一个if:递归的终止条件,如果链表1为空 或者 链表2为空就返回
- 第二个if:如果链表1的节点值 <= 链表2的节点值,则继续下一层的递归比较
- else部分:如果链表1的节点值 > 链表2的节点值,则继续下一层递归比较
后面我们就开始不断比较,不断调用下一层递归函数
每一步中,具体执行到哪段代码,我用红色标记出来了:
<<< 左右滑动见更多 >>>
- 上图中第一步、第二步:当链表1的节点1 <= 链表2的节点1时候,让链表1的节点1.next指向下一层递归函数,但此时程序并没有执行完,链表1的节点1.next指向的还是一个未知的节点,到底会指向谁,需要等后面的递归函数执行完返回后才知道
- 第三步、第四步:链表1的节点2>链表2的节点1,让链表2的节点1.next指向下一层递归函数,具体指向谁需要等后面的递归函数执行完返回后才知道
- 第五步、第六步:链表1的节点2<=链表2的节点3,让链表1的节点2.next 指向下一层递归函数
- 第七步、第八步:链表2的节点3.next 指向下一层的递归函数
- 第九步、第十步:链表1的节点4.next 指向下一层递归函数
经过上面 10 步之后,递归调用就到头了,因为上图中最后一张图,最右边的黑色方框里只剩下一个4
了。此时就会触发递归终止条件,然后不断返回
<<< 左右滑动见更多 >>>
再来分析一下上图中递归函数是怎么返回的:
- 第11步:此时链表1为空,于是触发递归终止条件,返回链表2的节点4
- 第12步:链表2的节点4返回到上一层递归函数,上一层函数是链表1的节点4,于是链表1的节点4.next 就指向 链表2的节点4
- 第13步:链表1的节点4返回
- 第14步:链表2的节点4返回到上一层递归函数,上一层函数是链表2的节点3,于是链表2的节点3.next 就指向 链表1的节点4
- 第15步:链表2的节点3返回
- 第16步:链表2的节点3返回到上一层递归函数,上一层的函数是链表1的节点2,于是链表1的节点2.next 就指向 链表2的节点3
- 第17步:链表1的节点2返回
- 第18步:链表1的节点2返回到上一层递归函数,上一层函数是链表2的节点1,于是链表2的节点1.next 就指向 链表1的节点2
- 第19步:链表2的节点1返回
- 第20步:链表2的节点1返回到上一层递归函数,上一层函数是链表的节点1,于是链表1的节点1.next 就指向 链表2的节点1
如下图所示,最后返回链表1的节点1,此时两个链表就被合并成一个链表了