【刷题之路】LeetCode 21. 合并两个有序链表
- 1、题目描述
- 二、解题
- 1、方法1——直接合并
- 1.1、思路分析
- 1.2、代码实现
- 2、方法2——递归
- 2.1、思路分析
- 2.2、代码实现
1、题目描述
原题连接: 21. 合并两个有序链表
题目描述:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 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]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列
二、解题
1、方法1——直接合并
1.1、思路分析
直接合并的思路就是使用两个指针分别遍历两个链表,每次都比较两个指针所指向的节点的值的大小,每次优先将值较小的节点尾插到一个新的链表之中:
而为了方便新链表的尾插,我们还需要一个tail指针来记录新链表的尾节点:
只要有其中一条链表遍历完毕,我们就可以跳出循环,然后我们需要判断还有哪一条链表为遍历完,然后直接将未遍历完的链表连接到tail的后面即可,例如:
这时候我们就直接将tail的next指向cur1即可。
1.2、代码实现
有了以上思路,那我们写起代码来也就水到渠成了:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
// 如果其中一个链表为空,则返回另一个即可
if (NULL == list1) {
return list2;
} else if (NULL == list2) {
return list1;
}
struct ListNode *head = NULL;
struct ListNode *tail = NULL;
while (list1 && list2) {
if (list1->val < list2->val) {
if (NULL == head) {
head = list1;
tail = list1;
} else {
tail->next = list1;
tail = tail->next;
}
list1 = list1->next;
} else {
if (NULL == head) {
head = list2;
tail = list2;
} else {
tail->next = list2;
tail = tail->next;
}
list2 = list2->next;
}
}
// 判断是否还有链表未连接完
if (list1) {
tail->next = list1;
} else if (list2) {
tail->next = list2;
}
return head;
}
时间复杂度:O(m+n),其中m和n分别是两条链表的长度,最坏情况下我们需要将两条链表全都遍历一遍。
空间复杂度:O(1),我们只需要用到常数级的额外空间。
2、方法2——递归
2.1、思路分析
其实从上面的解法中我们也能得到启发想出一个大事化小的解法,因为我们每次的遍历只需要选出一个较小的节点,然后尾插到新链表之中。
所以我们可以将思路转化为,每次调用只需要选出较小的节点然后让较小的节点连接上后面合并后的链表即可,而后面的链表的合并也是用同样的递归方式来完成。
即:
当其中一条链表为空时,递归就可以停止。
2.2、代码实现
有了以上思路,那我们写起代码来也就水到渠成了:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
if (NULL == list1) {
return list2;
} else if (NULL == list2) {
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;
}
}
时间复杂度:O(m+n),其中m和n分别为两个链表的长度。
空间复杂度:O(m+n),m和n分别为两个链表的长度,空间复杂度递归调用的层数,最坏情况下,对于两个链表的每个节点我们都要调用一次,因此空间复杂度为O(m+n)。