链表——合并链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
- 创建一个虚拟头节点(dummy),方便操作,不用处理头节点为空的边界情况
- 用一个指针cur遍历新链表
- 同时遍历两个原链表,每次把值更小的节点接到新链表后面
- 当其中一个链表遍历完,直接把另一个链表剩余部分拼接上去
- 返回虚拟头节点的下一个节点(真正的新链表头)
完整代码实现如下:
#include
using namespace std;
// 定义链表节点结构
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* l1, ListNode* l2) {
// 创建虚拟头节点,避免处理空指针边界
ListNode* dummy = new ListNode(-1);
// 工作指针,用于构建新链表
ListNode* cur = dummy;
// 同时遍历两个链表,取较小节点
while (l1 != nullptr && l2 != nullptr) {
if (l1->val < l2->val) {
cur->next = l1;
l1 = l1->next;
} else {
cur->next = l2;
l2 = l2->next;
}
cur = cur->next;
}
// 拼接剩余未遍历完的链表
cur->next = (l1 != nullptr) ? l1 : l2;
// 返回真正的头节点
return dummy->next;
}
};
// ===================== 测试代码 =====================
// 打印链表
void printList(ListNode* head) {
while (head != nullptr) {
cout << head->val << " ";
head = head->next;
}
cout << endl;
}
int main() {
// 构建链表1:1 -> 2 -> 4
ListNode* l1 = new ListNode(1);
l1->next = new ListNode(2);
l1->next->next = new ListNode(4);
// 构建链表2:1 -> 3 -> 4
ListNode* l2 = new ListNode(1);
l2->next = new ListNode(3);
l2->next->next = new ListNode(4);
Solution sol;
ListNode* merged = sol.mergeTwoLists(l1, l2);
// 输出结果:1 1 2 3 4 4
printList(merged);
return 0;
}
代码说明:
- 虚拟头节点:dummy 是一个临时节点,让我们不用处理新链表为空的情况
2.** 循环比较**:只要两个链表都没走完,就每次选值更小的节点加入新链表 - 拼接剩余节点:一个链表走完后,另一个剩下的节点直接全部接上
- 时间复杂度:O(n+m),n、m 是两个链表长度,只需遍历一次
5.** 空间复杂度**:O(1),只使用了常数个指针,没有新建节点
为什么加了虚拟头节点就不用处理新链表为空的情况?
因为虚拟头结点永远存在,所以新链表永远不是真正空的,cur->next 永远安全可写。
正常没有dummy会遇到什么麻烦?
ListNode* head = nullptr;
ListNode* cur = nullptr;
if (l1->val < l2->val) {
if (head == nullptr) { // 必须判断!
head = l1;
cur = head;
} else {
cur->next = l1;
cur = cur->next;
}
}
这里必须写if(head == nullptr)
因为:
- 最开始 head = nullptr
- 第一个节点要直接赋值给 head
- 否则 cur->next 会空指针崩溃
这就是:新链表为空时要特殊处理
浙公网安备 33010602011771号