力扣hot100 - 合并两个有序链表21

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;
}

代码逻辑简述:

  1. 哨兵节点 (dummy):我们创建了一个虚拟头节点。这使得我们无需编写额外的逻辑来判断"新链表的头是来自 l1 还是 l2",也无需在循环中判断"结果链表是否为空"。

  2. 双指针比较 :只要两个链表都不为空,就比较当前的头节点值。较小的那个节点被连接到 tail(结果链表的尾部),然后该链表的指针后移。

  3. 处理剩余部分 :当循环结束时,意味着其中一个链表已经空了。因为原链表是有序的,我们只需要直接把另一个非空链表的剩余部分直接链到 tail->next 即可,无需继续遍历。

复杂度分析:

  • 时间复杂度:O(n + m),其中 n 和 m 分别是两个链表的长度。我们需要遍历两个链表的每一个节点一次。

  • 空间复杂度:O(1)。我们只是调整了现有节点的指针,除了几个辅助指针外,没有分配新的节点空间。

相关推荐
小尧嵌入式9 分钟前
C++11线程库的使用(上)
c语言·开发语言·c++·qt·算法
蓝色汪洋17 分钟前
luogu填坑
开发语言·c++·算法
小年糕是糕手31 分钟前
【C++同步练习】类和对象(三)
开发语言·jvm·c++·程序人生·考研·算法·改行学it
Learner__Q33 分钟前
每天五分钟:leetcode动态规划-递归与递推_day2
算法·深度优先
代码游侠36 分钟前
学习笔记——Linux内核链表
linux·运维·笔记·学习·算法·链表
sheeta199837 分钟前
LeetCode 每日一题笔记 日期:2025.12.14 题目:2147.分隔长廊的方案数
linux·笔记·leetcode
发疯幼稚鬼39 分钟前
插入排序与冒泡排序
c语言·数据结构·算法·排序算法
小年糕是糕手42 分钟前
【C++同步练习】内存管理
开发语言·jvm·数据结构·c++·程序人生·算法·改行学it
灵感__idea1 小时前
Hello 算法:以“快”著称的哈希
前端·javascript·算法
ACERT3331 小时前
05-矩阵理论复习第五章 向量与矩阵范数
python·算法·矩阵