剑指Offer每日一题09
面试题25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例:
输入:1 --> 2 --> 5 --> null 2 --> 4 --> 7 --> null
输出:1 --> 2 --> 2 --> 4 --> 5 --> 7 --> null
限制:0 <= 链表长度 <= 1000
方法一:正常哨兵结点求解
class Solution {
public ListNode mergeLists(ListNode l1, ListNode l2) {
ListNode prehead = new ListNode(-1); //哨兵结点prehead
ListNode prev = prehead;
while (l1 != null && l2 != null) {
if (l1.value <= l2.value) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
//循环结束时,两表中至多有一个是非空的;
//由于均有序,所以非空链表包含的所有元素都比前面已经合并的链表中的所有元素要大,直接拼上返回即可
prev.next = l1 == null ? l2 : l1;
return prehead.next;
}
}
方法二:递归
搜得经典递归方法(1945 J. von Neumann)
递归地定义在两个链表里的 merge 操作(忽略边界情况,比如空链表等)
即两个链表头部较小的一个与剩下元素的 merge 操作结果合并。
再考虑边界情况,特殊的,如果 l1 或者 l2 一开始就是 null ,那么没有任何操作需要合并,所以我们只需要返回非空链表。否则,我们要判断 l1 和 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;
}
}
}
复杂度分析
时间复杂度:O(n + m)。 因为每次调用递归都会去掉 l1 或者 l2 的头元素(直到至少有一个链表为空),函数 mergeTwoList 中只会遍历每个元素一次。所以,时间复杂度与合并后的链表长度为线性关系。
空间复杂度:O(n + m)。调用 mergeTwoLists 退出时 l1 和 l2 中每个元素都一定已经被遍历过了,所以 n + m个栈帧会消耗 O(n + m) 的空间。