
cpp
#include <iostream>
#include <vector>
// 定义链表节点结构体
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* list1, ListNode* list2) {
// 创建一个哨兵节点(dummy node),它的值不重要。
// 使用哨兵节点可以避免处理新链表头节点为空的特殊情况。
ListNode* dummy = new ListNode(-1);
// tail 指针用于始终指向新链表的最后一个节点
ListNode* tail = dummy;
// 当两个链表都不为空时,比较它们头节点的值
while (list1 != nullptr && list2 != nullptr) {
if (list1->val <= list2->val) {
// 如果 list1 的值较小,将 list1 接到 tail 后面
tail->next = list1;
// list1 指针后移
list1 = list1->next;
} else {
// 如果 list2 的值较小,将 list2 接到 tail 后面
tail->next = list2;
// list2 指针后移
list2 = list2->next;
}
// tail 指针后移,准备连接下一个节点
tail = tail->next;
}
// 循环结束后,可能有一个链表还没有遍历完
// 直接将剩余部分接在 tail 后面即可(因为剩下的部分本身就是有序的)
if (list1 != nullptr) {
tail->next = list1;
} else if (list2 != nullptr) {
tail->next = list2;
}
// 保存结果头节点(哨兵节点的下一个节点)
ListNode* result = dummy->next;
// 释放哨兵节点内存(虽然在算法题中通常不强制,但在实际工程中是好习惯)
delete dummy;
return result;
}
};
// ==========================================
// 以下是辅助函数,用于本地测试代码运行结果
// ==========================================
// 辅助函数:根据数组创建链表
ListNode* createList(const std::vector<int>& vals) {
if (vals.empty()) return nullptr;
ListNode* head = new ListNode(vals[0]);
ListNode* current = head;
for (size_t i = 1; i < vals.size(); ++i) {
current->next = new ListNode(vals[i]);
current = current->next;
}
return head;
}
// 辅助函数:打印链表
void printList(ListNode* head) {
ListNode* curr = head;
std::cout << "[";
while (curr != nullptr) {
std::cout << curr->val;
if (curr->next != nullptr) std::cout << ",";
curr = curr->next;
}
std::cout << "]" << std::endl;
}
// 辅助函数:释放链表内存
void deleteList(ListNode* head) {
while (head != nullptr) {
ListNode* temp = head;
head = head->next;
delete temp;
}
}
int main() {
Solution solution;
// 示例 1
std::cout << "示例 1:" << std::endl;
ListNode* l1 = createList({1, 2, 4});
ListNode* l2 = createList({1, 3, 4});
ListNode* merged1 = solution.mergeTwoLists(l1, l2);
std::cout << "输出: ";
printList(merged1);
deleteList(merged1); // 清理内存
// 示例 2
std::cout << "\n示例 2:" << std::endl;
ListNode* l3 = createList({});
ListNode* l4 = createList({});
ListNode* merged2 = solution.mergeTwoLists(l3, l4);
std::cout << "输出: ";
printList(merged2);
deleteList(merged2);
// 示例 3
std::cout << "\n示例 3:" << std::endl;
ListNode* l5 = createList({});
ListNode* l6 = createList({0});
ListNode* merged3 = solution.mergeTwoLists(l5, l6);
std::cout << "输出: ";
printList(merged3);
deleteList(merged3);
return 0;
}
代码逻辑简述:
-
哨兵节点 (
dummy):我们创建了一个虚拟头节点。这使得我们无需编写额外的逻辑来判断"新链表的头是来自 l1 还是 l2",也无需在循环中判断"结果链表是否为空"。 -
双指针比较 :只要两个链表都不为空,就比较当前的头节点值。较小的那个节点被连接到
tail(结果链表的尾部),然后该链表的指针后移。 -
处理剩余部分 :当循环结束时,意味着其中一个链表已经空了。因为原链表是有序的,我们只需要直接把另一个非空链表的剩余部分直接链到
tail->next即可,无需继续遍历。
复杂度分析:
-
时间复杂度:O(n + m),其中 n 和 m 分别是两个链表的长度。我们需要遍历两个链表的每一个节点一次。
-
空间复杂度:O(1)。我们只是调整了现有节点的指针,除了几个辅助指针外,没有分配新的节点空间。