题目链接:21. 合并两个有序链表
解法一 迭代
利用迭代法求解,大概是正常人看到此题的第一想法。求解思路与“合并两个有序数组”如出一辙,不同的是,链表不需要开辟新空间用于存放元素。
实现步骤如下:
- 建立一个新的头节点
newHead
,用于最后返回有序的链表。同时,初始化节点cur.next = newHead
,用于向newHead
指向的链表中存放元素。 - 比较
l1.val
与l2.val
的大小。若l1.val <= l2.val
,cur.next = l1
,切记!之后需要维护l1
和cur
,再进行下一次循环。 若l2.val < l1.val
,操作同理。 - 循环在
l1
和l2
其一指向null
时停止,此时要做的就是将另一个非空的链表接在cur
后面。 - 最后,返回
newHead.next
。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode newHead = new ListNode();
ListNode cur = newHead;
while (l1 != null && l2 != null){
if (l1.val <= l2.val){
cur.next = l1;
l1 = l1.next;
}else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1 == null ? l2 : l1;
return newHead.next;
}
复杂度分析
- 时间复杂度:O(n+m),两个链表长度分别为n和m。
- 空间复杂度:O(1),没开新空间。
解法二 递归
递归,本质上是将原来的问题,转化为更小规模的同一问题。
递归函数一般都由两部分组成:求解最基本的问题、把原问题转化成更小的问题。
求解最基本的问题,也就是递归的终止条件。
把原问题转化成更小的问题(递归的等价关系),反过来说,就是用更小规模问题的解去构建原规模的问题。**其中,需要特别注意递归函数的”宏观“语意。**递归函数的目的在于完成一个功能,在求解过程中,我们需要对递归的目的有一个具体明确的认识。
对于此问题,递归函数构建的思路如下:
- 终止条件:两个链表中其一为空
- 等价关系:递归函数的目的很明确,返回一个合并好的有序链表,但它不仅仅是合并好的有序链表,且这个有序链表的最小值(头节点)是不小于原规模问题中
l1.val
和l2.val
,因此,递归函数的入参应该是l1.val
和l2.val
直接小的值的下一个节点,和大的值所在的节点。例如,若l1.val <= l2.val
,则入参为(l1.next, l2)
,注意!不能是(l1.next, l2.next)
,因为这样可能会存在l2.val > l1.next.val
的情况,这与返回值”不小于“的条件相悖。 - 之后,得到了返回的有序链表,只需与较小值所在的节点合并即可。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null)
return l2;
else if (l2 == null)
return l1;
if (l1.val <= l2.val){
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
复杂度分析
- 时间复杂度:O(n+m),额,不是很懂。。。官方说取决于合并后链表的长度
- 空间复杂度:O(n+m),递归深度最大为n+m。
小结
链表,真乃练习递归的神器!
以及,《理解》还是不到位,又是菜菜的一天~