21. Merge Two Sorted Lists
21. Merge Two Sorted Lists是Leetcode面试榜单Top150链表类第3题,需要把两个已经有序的单链表,合并为一个有序链表返回。可以自上而下的递归或者自下而上的迭代实现。
链表和数组的两个不同特点是链表不能通过索引直接读到节点,以及链表是通过子节点指针遍历节点。在设计算法时,和数组类数据类似,可以直观考虑用辅助内存空间(额外数组,栈,队列,集合,字典等数据结构),也可以考虑只用一个或者多个节点指针遍历计算在原列表中记录修改来优化空间复杂度,降低空间使用。相比于用数组存储的两个有序数列的合并通过索引下标实现,链表类存储的有序数列合并通过next指针只能实现从头到尾的遍历但修改移动队列中元素位置仅需要修改它的前置节点,它自己和目标位置的前置节点的next指针就完成。
解法1:自上而下的递归实现,时间和空间复杂度都是O(m + n),算法代码中没有直接分配辅助存储空间,但是递归调用时,每一次的递归调用系统都会需要保留调用栈的存储。递归/迭代算法的设计可以套用“C教授的神奇4步法”:
- 定义size-n问题,方法定义
- 找到循环递归结束条件,方法返回的结束条件
- 定义更小问题size-m问题(m < n,常见m = n - 1, n/2,等)
- 使用size-m问题的解解决原size-n问题。
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;
}
}
}
解法2:自下而上的迭代实现,手动使用多个节点指针,遍历操作数据序列的合并,减少递归调用的存储空间使用,因此时间复杂度和递归实现一样的O(m+n),但空间复杂度减低到O(1).
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
// maintain an unchanging reference to node ahead of the return node.
ListNode prehead = new ListNode(-1);
ListNode prev = prehead;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
// At least one of l1 and l2 can still have nodes at this point, so connect
// the non-null list to the end of the merged list.
prev.next = l1 == null ? l2 : l1;
return prehead.next;
}
}