文章目录
- Tag
- 题目来源
- 题目解读
- 解题思路
- 方法一:递归
- 方法二:迭代
- 写在最后
Tag
【递归】【迭代】【链表】
题目来源
21. 合并两个有序链表
题目解读
合并两个有序链表。
解题思路
一种朴素的想法是将两个链表中的值存入到数组中,然后对数组进行升序排序,最后将排序好的数组还原回链表,这是一种可行的思路,但是没有充分利用题目已知的两个链表有序的条件,大家可以自行尝试,练习基础语法与建立链表节点的知识。
方法一:递归
我们记两个链表的头节点分别为 l1
和 l2
,在合并两个链表的时候会遇到以下三种情况:
l1
为空,直接返回l2
;l2
为空,直接返回l1
;- 两节点都不为空,那么又会分为两种情况:
l1
节点值小于l2
节点值,那么l1
节点将会是合并后的节点新的头节点,剩下的部分是l1->next
和l2
合并的节点,而合并l1->next
和l2
是合并l1
和l2
的子问题,也可以使用mergeTwoLists
函数来解答,于是有l1->next = mergeTwoLists(l1->next, l2)
,并返回l1
;- 同理,
l2
节点值小于l1
节点值时,有l2->next = mergeTwoLists(l1, l2->next)
,并返回l1
。
- 以上这种将问题转换为原问题的子问题的方法,称为递归方法。递归方法是一种边调用边填充的方法。
实现代码
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if (list1 == nullptr) {
return list2;
}
else if (list2 == nullptr) {
return list1;
}
else if (list1->val < list2->val) {
list1->next = mergeTwoLists(list1->next, list2);
return list1;
}
else {
list2->next = mergeTwoLists(list1, list2->next);
return list2;
}
}
};
python3
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
if l1 is None:
return l2
elif l2 is None:
return l1
elif l1.val < l2.val:
l1.next = self.mergeTwoLists(l1.next,l2)
return l1
else:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2
复杂度分析
时间复杂度: O ( m + n ) O(m+n) O(m+n), m m m 和 n n n 分别为两个链表的长度,每个节点都是被递归调用一次。
空间复杂度:
O
(
m
+
n
)
O(m+n)
O(m+n),每调用一次函数 mergeTwoLists
都需要消耗栈空间,栈空间的大小取决于递归调用的深度。
方法二:迭代
迭代的方法是一种相对容易理解的方法。为了方便实现,我们定义一个哑节点 dummy
,ListNode* dummy = new ListNode(-1);
,最后只需要返回 dummy->next
。
我们再定义一个节点 prev
用来指向当前值较小的节点,初始 prev = dummy
,我们迭代枚举两链表中的节点:
prev
指向值较小的节点;- 值较小的节点更新为下一个节点,方便下一对节点的比较;
prev
更新为下一个节点,为存放下一个更小的节点做准备;- 如果有一个链表为空了,直接退出迭代循环;
- 需要注意这时候,两个链表中可能还有一个链表非空,需要将剩下的非空部分接在
prev
后面。
以上就是本题的迭代方法。
实现代码
C++
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy = new ListNode(-1);
ListNode* prev = dummy;
while(list1 && list2){
if(list1->val < list2->val){
prev->next = list1;
list1 = list1->next;
}
else{
prev->next = list2;
list2 = list2->next;
}
prev = prev->next;
}
prev->next = list1 ? list1 : list2;
return dummy->next;
}
};
python3
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(-1)
prev = dummy
while l1 and l2:
if l1.val < l2.val:
prev.next = l1
l1 = l1.next
else:
prev.next = l2
l2 = l2.next
prev = prev.next
prev.next = l1 if l1 is not None else l2
return dummy.next
复杂度分析
时间复杂度: O ( m + n ) O(m+n) O(m+n), m m m 和 n n n 分别为两个链表的长度,每个节点都是被递归调用一次。
空间复杂度: O ( 1 ) O(1) O(1)。
写在最后
如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。
如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。
最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。