题目:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 2:
输入:l1 = [], l2 = [] 输出:[]
示例 3:
输入:l1 = [], l2 = [0] 输出:[0]
提示:
- 两个链表的节点数目范围是
[0, 50]
-100 <= Node.val <= 100
l1
和l2
均按 非递减顺序 排列
非递归方法(迭代)
思路:
利用迭代的方式合并两个有序链表。我们可以维护一个新的头节点
dummy
,以及一个指针current
指向当前合并后的链表的末尾。然后,比较两个链表的头节点,将较小的节点接入到新链表中,并更新相应的指针,直到其中一个链表为空。最后,将剩余非空的链表直接接入到新链表的末尾。
实现步骤:
- 初始化一个
dummy
节点作为新链表的头部。- 使用
current
指针来表示当前合并后链表的最后一个节点。- 遍历两个链表,比较当前节点的值,将较小的节点接入到新链表中,并移动相应的指针。
- 将剩余非空的链表直接接入到新链表的末尾。
- 返回
dummy.next
,即合并后的链表的头节点。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1);
ListNode current = dummy;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
current.next = l1;
l1 = l1.next;
} else {
current.next = l2;
l2 = l2.next;
}
current = current.next;
}
// 将剩余部分接到新链表的末尾
current.next = (l1 != null) ? l1 : l2;
return dummy.next;
}
复杂度分析:
- 时间复杂度:O(n),其中 n 是两个链表中节点总数的较小值。因为每个节点只会被访问一次。
- 空间复杂度:O(1),除了存储合并后的链表之外,只使用了常量级的额外空间。
递归方法
思路:
递归的思路是比较两个链表的头节点,将较小的头节点作为当前节点的下一个节点,并递归地处理剩余的部分。递归函数可以定义为:
- 如果其中一个链表为空,直接返回另一个链表。
- 否则,比较两个链表的头节点,将较小的节点的
next
指向递归合并后的结果。
实现步骤:
- 如果 l1 为空,返回 l2;如果 l2 为空,返回 l1。
- 比较 l1 和 l2 的头节点,将较小节点的
next
指向递归合并后的结果。- 返回较小节点作为当前递归层的头节点。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
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),其中 n 是两个链表中节点总数的较小值。因为每个节点只会被访问一次。
- 空间复杂度:O(n),递归调用的栈空间。在最坏情况下,递归深度可以达到 n。
这两种方法都可以有效地合并两个有序链表,并返回一个新的有序链表。选择使用迭代方法还是递归方法取决于个人偏好和具体的应用场景,但从效率上来说,迭代方法通常更具有优势。