Leetcode 21. 合并两个有序链表

270 阅读3分钟

原题链接: 21. 合并两个有序链表 - 力扣(Leetcode)

tag: 链表.

一. 题目

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

二. 题解

本题我们通过将两个链表的节点 尾插 到合并的链表中来合并链表.

由于尾插第一个节点时合并的链表中没有节点, 即 tail == nullptr, 无法通过 tail->next = l1 或者 tail->next = l2 来尾插节点, 需要分类讨论 (特判 tail 是否为空).

为了避免这种额外的讨论, 我们引入虚拟头节点(dummy)的概念.

设置虚拟头节点, 即在链表的头节点前新插入一个节点. 如此, 链表的头节点便有了前驱节点(即dummy), 尾插时就不需要进行额外的讨论.

合并前.

image.png

设置一个虚拟头节点 dummy.

ListNode* dummy = new ListNode();

image.png

定义尾指针 tail, 指向 dummy.

ListNode* tail = dummy;

image.png

开始合并链表.

image.png

l1-val == l2->val

image.png

l2 所指节点尾插.

tail->next = l2;

image.png

更新 l2.

l2 = l2->next;

image.png

更新 tail 指针.

tail = tail->next;

image.png

继续合并链表.

image.png

l1-val < l2->val

image.png

l1 所指节点尾插.

tail->next = l1;

image.png

更新 l1.

l1 = l1->next;

image.png

更新 tail 指针.

tail = tail->next;

image.png

继续合并链表.

image.png

l1-val < l2->val

image.png

l1 所指节点尾插.

tail->next = l1;

image.png

更新 l1.

l1 = l1->next;

image.png

更新 tail 指针.

tail = tail->next;

image.png

l1 == nullptr, while 循环终止.

image.png

l2 != nullptr

image.png

l2 后的所有节点接到合并的链表上去.

tail->next = l2;

image.png

返回合并链表的头节点.

ListNode* head = dummy->next;

image.png

delete dummy;

image.png

合并后.

image.png

三. 复杂度分析

时间复杂度: O(M + N), M 和 N 分别为两个链表的长度. 每次循环迭代中, l1l2 中只有一个节点会被放进合并的链表中, 因此 while 循环的次数不会超过两个链表的长度之和. 除循环外的其他所有操作的时间复杂度都是常数级别的, 因此时间复杂度为 O(M + N).

空间复杂度: O(1).

四. 代码

/**
 * Definition for singly-linked list.
 * 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();    // 设置一个虚拟头节点
        ListNode* tail = dummy;    // 定义尾指针 tail
        while (l1 && l2) {
            if (l1->val < l2->val) {
                tail->next = l1;    // 将 l1 当前所指节点插入合并链表中
                l1 = l1->next;    // 更新 l1
            } else {
                tail->next = l2;    // 将 l2 当前所指节点插入合并链表中
                l2 = l2->next;    // 更新 l2
            }
            tail = tail->next;    // 更新 tail 指针
        }

        if (l1) tail->next = l1;    // 将 l1 中剩余的节点接入合并链表中
        if (l2) tail->next = l2;    // 将 l2 中剩余的节点接入合并链表中

        ListNode* head = dummy->next;
        delete dummy;
        return head;
    }
};