☰
【链表-4】21.合并两个有序链表
2026/10/16 14:16:38 网站建设 项目流程

题目描述:

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []输出:[]

示例 3:

输入:l1 = [], l2 = [0]输出:[0]

解题思路:

我的思路是:先把两个链表的值按顺序合并到一个 vector,再用 vector 的值新建链表。

具体步骤:

  1. 处理边界:如果有一个链表为空,直接返回另一个

  2. 双指针合并:用list1和list2逐个比较,把较小的值存入value

  3. 处理剩余:把没遍历完的链表剩余部分全部存入value

  4. 重建链表:遍历value,逐个new ListNode,连接成新链表

用例子走一遍:

list1 = 1 -> 2 -> 4,list2 = 1 -> 3 -> 4

步骤比较存入 value
11 <= 11(list1 后移)
22 > 11(list2 后移)
32 <= 32(list1 后移)
44 > 33(list2 后移)
54 <= 44(list1 后移)
6list1 为空4(list2 后移)

value = [1, 1, 2, 3, 4, 4]

重建链表:1 -> 1 -> 2 -> 3 -> 4 -> 4✅

代码实现:

class Solution { public: ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) { if(list1==nullptr&&list2==nullptr) return list1; if(list1==nullptr) return list2; if(list2==nullptr) return list1; vector<int> value; while(list1!=nullptr&&list2!=nullptr){ if(list1->val<=list2->val){ value.push_back(list1->val); list1=list1->next; } else{ value.push_back(list2->val); list2=list2->next; } } if(list1==nullptr){ while(list2!=nullptr){ value.push_back(list2->val); list2=list2->next; } } else{ while(list1!=nullptr){ value.push_back(list1->val); list1=list1->next; } } ListNode *head = nullptr; ListNode *tail = nullptr; for (int x : value) { ListNode *node = new ListNode(x); if (head == nullptr) { head = node; tail = node; } else { tail->next = node; tail = node; } } return head; } };

复杂度分析:

维度复杂度说明
时间复杂度O(n + m)遍历两个链表各一次,重建链表一次
空间复杂度O(n + m)vector 存储所有值 + 新建链表节点

问题:额外使用了 O(n + m) 的空间,看了题解后发现这道题的标准解法可以做到O(1) 空间(原地合并)。

最优解法:原地合并(O(1) 空间)

核心思路:

不需要 vector,直接修改指针,把两个链表的节点按顺序串起来。

用虚拟头结点(dummy node)简化操作:

  • dummy是虚拟头结点,不存实际值

  • curr指向当前合并后的最后一个节点

  • 每次比较list1和list2,把较小的节点接到curr->next

  • 最后把剩余的链表直接接上

具体过程示例:

list1 = 1 -> 2 -> 4,list2 = 1 -> 3 -> 4

dummy -> null curr = dummy 第1步: 1 <= 1, 接 list1 的 1 dummy -> 1 curr = 1, list1 = 2 第2步: 2 > 1, 接 list2 的 1 dummy -> 1 -> 1 curr = 1, list2 = 3 第3步: 2 <= 3, 接 list1 的 2 dummy -> 1 -> 1 -> 2 curr = 2, list1 = 4 第4步: 4 > 3, 接 list2 的 3 dummy -> 1 -> 1 -> 2 -> 3 curr = 3, list2 = 4 第5步: 4 <= 4, 接 list1 的 4 dummy -> 1 -> 1 -> 2 -> 3 -> 4 curr = 4, list1 = null 第6步: list1 为空, 接 list2 的剩余部分 dummy -> 1 -> 1 -> 2 -> 3 -> 4 -> 4 返回 dummy->next ✅

代码实现:

class Solution { public: ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) { ListNode *dummy = new ListNode(0); // 虚拟头结点 ListNode *curr = dummy; while (list1 != nullptr && list2 != nullptr) { if (list1->val <= list2->val) { curr->next = list1; list1 = list1->next; } else { curr->next = list2; list2 = list2->next; } curr = curr->next; } // 接上剩余的链表 curr->next = (list1 != nullptr) ? list1 : list2; return dummy->next; } };

复杂度分析:

维度复杂度
时间复杂度O(n + m),遍历两个链表各一次
空间复杂度O(1),只用了几个指针(不算虚拟头结点)

两种方法对比:

对比维度我的方法(vector重建)原地合并
时间复杂度O(n + m)O(n + m)
空间复杂度O(n + m)O(1)
是否修改原链表❌ 不修改✅ 修改
是否需要新建节点✅ 需要 new n+m 个❌ 不需要
代码复杂度较长极简
推荐度⭐⭐⭐⭐⭐⭐⭐⭐

关键细节:

1. 虚拟头结点的作用

ListNode *dummy = new ListNode(0); ListNode *curr = dummy;
  • dummy不存实际值,只是方便返回结果

  • 如果不使用 dummy,需要单独处理"第一个节点"的情况,代码会复杂很多

  • 返回时返回dummy->next即可

2. 为什么最后直接接剩余部分?

curr->next = (list1 != nullptr) ? list1 : list2;

因为list1和list2本身已经是有序的,剩余的部分不需要再逐个比较,直接接上即可。

总结:

要点说明
我的代码时间 O(n+m),空间 O(n+m),正确但空间不是最优
最优解法虚拟头结点 + 原地合并,时间 O(n+m),空间 O(1)
核心思想逐个比较,把较小的节点接到结果链表
关键技巧虚拟头结点简化边界处理

这道题是链表合并的经典题,掌握后可以解决很多链表题(比如合并 K 个有序链表、排序链表等)。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询