21. 合并两个有序链表(简单)
Blind 75 › 21. 合并两个有序链表(简单) (第 2 章)
题目
给定两个升序排列的单链表,把它们合并成一条新的升序链表并返回。新链表由两条原链表的节点直接拼接而成(可以复用原节点,不要求新建节点)。
换句话说:每次只看两条链当前最前面的节点,把值较小的那个接到结果末尾;一条链走完后,把另一条剩余部分整段接上即可。
自造示例:list1 = [1,2,4]、list2 = [1,3,4] → [1,1,2,3,4,4];list1 = [2,5]、list2 = [1,3,6] → [1,2,3,5,6]。
约束:两条链表的节点总数在 0 ~ 50;节点值 -100 ~ 100;两条链表均按非递减顺序排列。
思路
一句话:两条链都已有序,每个位置只要在两个“当前头节点”里取较小者接上即可。
先想最直白的:把所有节点值收集到一个数组,整体排序,再重建链表。这样一定正确,但时间 O((m+n) log(m+n))、空间 O(m+n),白白浪费了“两条链已经各自有序”的条件。
再想“边比较边接”:既然每条链内部已经排好,那么结果链的下一个最小值,只可能是 list1 当前头节点或 list2 当前头节点中较小的那个。每次取走它、推进对应指针,就像归并排序里的合并一步。
关键观察:这个“每次取较小者”的贪心不会错过任何节点——被跳过的一方之后只会变大或相等,不可能产生更小的值,所以合并结果必然整体升序。
实现上用一个虚拟头节点 dummy 统一“结果为空”和“结果非空”两种情况,避免特判第一个节点。
解法
设 tail 始终指向结果链的最后一个节点,dummy.next 是结果链真正的头:
- 当两条链都非空:比较
list1.val与list2.val,把较小节点接到tail.next,对应链指针后移,tail也后移。 - 循环结束说明至少一条链为空,把仍非空的那条整段接到
tail.next。 - 返回
dummy.next。
| 步骤 | list1 当前 | list2 当前 | 取谁 | 结果链 |
|---|---|---|---|---|
| 1 | 1 | 1 | list1 的 1 | [1] |
| 2 | 2 | 1 | list2 的 1 | [1,1] |
| 3 | 2 | 3 | list1 的 2 | [1,1,2] |
| 4 | 4 | 3 | list2 的 3 | [1,1,2,3] |
| 5 | 4 | 4 | list1 的 4 | [1,1,2,3,4] |
| 6 | 空 | 4 | 整段接上 | [1,1,2,3,4,4] |
相等时取任意一条都行;代码里写 <= 取 list1,是为了保持稳定(原 list1 在前)。
下图把上面 3 个步骤串成流程,并逐步演示:


C++
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode dummy(0); // 栈上哨兵,避免 new 造成内存泄漏
ListNode* tail = &dummy;
while (list1 && list2) {
if (list1->val <= list2->val) {
tail->next = list1;
list1 = list1->next;
} else {
tail->next = list2;
list2 = list2->next;
}
tail = tail->next;
}
tail->next = list1 ? list1 : list2; // 接上没走完的那条
return dummy.next;
}
};Python
Python 3 示例
还没有评论,来说两句?