一、题目描述
给定两个 按非递减顺序排列 的链表 list1 和 list2,将它们合并为一个新的 升序链表 并返回。
新链表是通过 拼接给定的两个链表的所有节点组成的。
示例 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均按 非递减顺序 排列
二、解题思路
由于两个链表已经是 有序链表 ,我们可以使用 双指针思想 来解决。
思路如下:
-
创建一个 虚拟头节点 dummy,用于简化链表操作。
-
定义一个指针
cur指向当前新链表的末尾。 -
同时遍历
list1和list2:-
比较两个节点的值
-
将较小的节点接到新链表后面
-
移动对应链表指针
-
-
当其中一个链表遍历结束时,直接把 剩余链表接到新链表末尾。
示意过程:
list1: 1 -> 2 -> 4
list2: 1 -> 3 -> 4
比较过程:
1 vs 1 -> 取 list1
2 vs 1 -> 取 list2
2 vs 3 -> 取 list1
4 vs 3 -> 取 list2
4 vs 4 -> 取 list1
剩余 -> 接上 list2
最终结果:
1 -> 1 -> 2 -> 3 -> 4 -> 4
三、C语言代码实现
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
// 创建虚拟头节点
struct ListNode dummy;
struct ListNode *cur = &dummy;
dummy.next = NULL;
// 同时遍历两个链表
while(list1 != NULL && list2 != NULL)
{
if(list1->val <= list2->val)
{
cur->next = list1;
list1 = list1->next;
}
else
{
cur->next = list2;
list2 = list2->next;
}
cur = cur->next;
}
// 连接剩余链表
if(list1 != NULL)
cur->next = list1;
else
cur->next = list2;
return dummy.next;
}
四、复杂度分析
时间复杂度
O(n + m)
-
n为list1的长度 -
m为list2的长度
每个节点最多访问一次。
空间复杂度
O(1)
只使用了几个指针变量,没有额外空间开销。
五、为什么使用虚拟头节点?
如果不使用 dummy,新链表的 第一个节点需要单独处理。
例如:
head = list1 or list2 ?
代码逻辑会变复杂。
使用虚拟头节点后:
dummy -> 1 -> 1 -> 2 -> 3 -> 4 -> 4
最终返回:
dummy.next
这样可以统一处理所有节点,代码更加简洁清晰。
六、递归解法(扩展)
除了迭代方法,还可以使用 递归 来实现。
思路:
-
如果
list1较小,则list1->next与list2继续合并 -
如果
list2较小,则list2->next与list1继续合并
代码如下:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if(list1 == NULL) return list2;
if(list2 == NULL) return list1;
if(list1->val < list2->val)
{
list1->next = mergeTwoLists(list1->next, list2);
return list1;
}
else
{
list2->next = mergeTwoLists(list1, list2->next);
return list2;
}
}
七、总结
本题是 链表中的经典基础题目,核心思想是:
⭐ 双指针 + 有序链表合并
关键点:
-
使用 虚拟头节点简化操作
-
每次选择 较小节点接入新链表
-
最后连接 剩余链表
时间复杂度:
O(n + m)
空间复杂度:
O(1)
这道题也是很多链表题目的基础,例如:
-
23. 合并 K 个升序链表
-
148. 排序链表
-
归并排序链表
掌握这道题,对于理解 链表合并思想 非常重要。