题目
来源:LeetCode.
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
- 两 个 链 表 的 节 点 数 目 范 围 是 [ 0 , 50 ] 两个链表的节点数目范围是 [0, 50] 两个链表的节点数目范围是[0,50]
- − 100 < = N o d e . v a l < = 100 -100 <= Node.val <= 100 −100<=Node.val<=100
- l 1 和 l 2 均 按 非 递 减 顺 序 排 列 l1 和 l2 均按 非递减顺序 排列 l1和l2均按非递减顺序排列
接下来看一下解题思路:
思路一:递归:
如果
l
1
l1
l1 或者
l
2
l2
l2 一开始就是空链表 ,那么没有任何操作需要合并,所以我们只需要返回非空链表;
1. 比较两个链表的结点;
2. 结点小的链表递归调用下一个结点;
3. 并且将该结点指向递归返回的结果;
4. 如果两个链表有一个为空,递归结束;
public static 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
)
O(n + m)
O(n+m),其中
n
n
n 和
m
m
m 分别为两个链表的长度。因为每次调用递归都会去掉
l
1
l1
l1 或者
l
2
l2
l2 的头节点(直到至少有一个链表为空),函数
m
e
r
g
e
T
w
o
L
i
s
t
mergeTwoList
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 分别为两个链表的长度。递归调用
m
e
r
g
e
T
w
o
L
i
s
t
s
mergeTwoLists
mergeTwoLists 函数时需要消耗栈空间,栈空间的大小取决于递归调用的深度。结束递归调用时
m
e
r
g
e
T
w
o
L
i
s
t
s
mergeTwoLists
mergeTwoLists 函数最多调用
n
+
m
n+m
n+m 次,因此空间复杂度为
O
(
n
+
m
)
O(n+m)
O(n+m)。
思路二:迭代:
1. 首先,我们设定一个哨兵节点
p
r
e
h
e
a
d
prehead
prehead ,这可以在最后让我们比较容易地返回合并后的链表;
2. 维护一个
p
r
e
v
prev
prev 指针,我们需要做的是调整它的
n
e
x
t
next
next 指针;
3. 然后,我们重复以下过程,直到
l
1
l1
l1 或者
l
2
l2
l2 指向了
n
u
l
l
null
null :如果
l
1
l1
l1 当前节点的值小于等于
l
2
l2
l2 ,我们就把
l
1
l1
l1 当前的节点接在
p
r
e
v
prev
prev 节点的后面同时将
l
1
l1
l1 指针往后移一位;
4. 否则,我们对
l
2
l2
l2 做同样的操作。不管我们将哪一个元素接在了后面,我们都需要把
p
r
e
v
prev
prev 向后移一位;
5. 在循环终止的时候,
l
1
l1
l1 和
l
2
l2
l2 至多有一个是非空的;
由于输入的两个链表都是有序的,我们只需要简单地将非空链表接在合并链表的后面,并返回合并链表即可。
public static ListNode mergeTwoLists1(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
if (l2 == null) {
return l1;
}
ListNode prehead = new ListNode(0);
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;
}
prev.next = (l1 == null ? l2 : l1);
return prehead.next;
}
总结
时间复杂度:
O
(
n
+
m
)
O(n + m)
O(n+m),其中
n
n
n 和
m
m
m 分别为两个链表的长度。因为每次循环迭代中,
l
1
l1
l1 和
l
2
l2
l2 只有一个元素会被放进合并链表中, 因此
w
h
i
l
e
while
while 循环的次数不会超过两个链表的长度之和。所有其他操作的时间复杂度都是常数级别的,因此总的时间复杂度为
O
(
n
+
m
)
O(n+m)
O(n+m)。
空间复杂度:
O
(
1
)
O(1)
O(1)。我们只需要常数的空间存放若干变量。