难度:简单
将两个升序链表合并为一个新的升序链表并返回。
新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
方法一:(暴力法)
题目中要求,新链表是不能开辟额外的空间的,而是去拼接原来的两个链表的所有结点。
思路:
- 这里的合并,就是从头结点开始比较 l 1 l1 l1 和 l 2 l2 l2 ,然后把较小的结点先加入最终要返回的链表之中。
- 所以需要有一个临时指针,指向链表合并的当前结点。
- 另外两个指针,分别指向原链表中正在被比较的两个结点
- 引入哑结点(也可以说是哨兵结点),可以让我们更容易地返回合并后的链表
代码过程:
- 我们维护一个 p r e v prev prev 指针 (最开始是指向哨兵结点的),我们需要做的是调整它的 n e x t next next 指针。
- 然后,我们重复以下过程,直到
l
1
l1
l1或者
l
2
l2
l2 指向了
n
u
l
l
null
null :
- 如果当前
l1.val<=l2.val
- 把 l 1 l1 l1 当前的节点接在 p r e v prev prev 节点的后面
- 同时将 l 1 l1 l1 指针往后移一位。
- 否则
- 把 l 2 l2 l2 当前的节点接在 p r e v prev prev 节点的后面
- 同时将 l 2 l2 l2 指针往后移一位。
- 不管我们将哪一个元素接在了后面,都需要把 p r e v prev prev 向后移一位。
- 如果当前
- 在循环终止的时候, l 1 l1 l1 和 l 2 l2 l2 至多有一个是非空的。
- 由于输入的两个链表都是有序的,所以不管哪个链表是非空的,它包含的所有元素都比前面已经合并链表中的所有元素都要大。
- 我们只需要简单地将非空链表接在合并链表的后面,并返回合并链表即可。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//构建哨兵结点
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;
}
// 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev.next= (l1 == null) ? l2 : l1;
return prehead.next;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-lian-biao-by-leetcode-solu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析:
- 时间复杂度: O ( n + m ) O(n+m) O(n+m) ,其中 n n n 和 m m m 分别为两个链表的长度。因为最坏的情况下,我们需要把两个链表都遍历一遍。时间复杂度和链表的长度在一个量级。
- 空间复杂度:
O
(
1
)
O(1)
O(1) 。我们只需要常数的空间存放若干变量。
方法二:(递归)
思路:
- 假设 l 1 l1 l1 的头结点的值较小,那么合并 l 1 l1 l1 和 l 2 l2 l2,等价于 l 1. n e x t l1.next l1.next 指向 l 1. n e x t l1.next l1.next 与 l 2 l2 l2 合并的结果。其中合并 l 1. n e x t l1.next l1.next 与 l 2 l2 l2 是原问题的一个子问题
- 子问题和原问题具有相同结构,考虑自上而下的递归
代码:
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null)
return l2;
if(l2==null)
return l1;
ListNode head = null;
if(l1.val<=l2.val){
head = l1;
head.next = mergeTwoLists(l1.next,l2);
}else{
head = l2;
head.next = mergeTwoLists(l2.next,l1);
}
return head;
}
}
复杂度分析:
- 时间复杂度: O ( n + m ) O(n+m) O(n+m) ,因为每次调用递归都会去掉 l 1 l1 l1 或者 l 2 l2 l2 的头节点(直到至少有一个链表为空),函数 mergeTwoList 至多只会递归调用每个节点一次。因此,时间复杂度取决于合并后的链表长度,即 O ( n + m ) O(n+m) O(n+m)。
- 空间复杂度: O ( n + m ) O(n+m) O(n+m) 。其中 n n n 和 m m m 分别为两个链表的长度。递归调用 mergeTwoLists 函数时需要消耗栈空间,栈空间的大小取决于递归调用的深度。结束递归调用时 mergeTwoLists 函数最多调用 n + m n+m n+m 次,因此空间复杂度为 O ( n + m ) O(n+m) O(n+m)。