合并两个有序链表

题面

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

分析

  • 显然,可以使用两个指针,遍历之后就将另一个链表的节点加入到另一个链表中

  • 代码实现

    /**
     * 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* list1, ListNode* list2) {
            if (list1 == nullptr)
                return list2;
            if (list2 == nullptr)
                return list1;
            ListNode *p1 = list1, *p2 = list2;
            ListNode *pre = nullptr, *res = p1;
            while (p2 != nullptr) {
                while (p1 != nullptr && p2->val > p1->val) {
                    pre = p1;
                    p1 = p1->next;
                }
                ListNode* temp = p2->next;
                if (pre != nullptr)
                    pre->next = p2;
                else
                    res = p2;
                p2->next = p1;
                p1 = p2;
                p2 = temp;
            }
            return res;
        }
    };
  • 可以添加一个虚拟头节点,来避免处理 pre == nullptr 的额外逻辑

    /**
     * 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* list1, ListNode* list2) {
            // 定义哑节点(哨兵节点),初始值任意,仅作为临时头节点
            ListNode* dummy = new ListNode(-1);
            // 定义一个指针cur,用于遍历并构建新链表
            ListNode* cur = dummy;
            // 当两个链表都不为空时,比较当前节点值,选择更小的接入新链表
            while (list1 != nullptr && list2 != nullptr) {
                if (list1->val <= list2->val) {
                    cur->next = list1;  // 接入list1的当前节点
                    list1 = list1->next;// list1指针后移
                } else {
                    cur->next = list2;  // 接入list2的当前节点
                    list2 = list2->next;// list2指针后移
                }
                cur = cur->next;        // cur指针后移,准备接入下一个节点
            }
    
            // 处理剩余的节点(其中一个链表已遍历完,直接接入另一个的剩余部分)
            cur->next = list1 != nullptr ? list1 : list2;
    
            // 哑节点的next才是真正的头节点,最后释放哑节点避免内存泄漏
            ListNode* res = dummy->next;
            delete dummy;
            return res;
        }
    };
  • 通过哑节点,可以将根据大小的比较,逐个将新节点移动到哑节点上,避免直接将节点挪到另一个链表上时的复杂逻辑

  • 也可以用递归来实现,递归逻辑如下

    merge(list1,list2)={list1[0]+merge(list1[1:],list2)list1[0]<list2[0]list2[0]+merge(list1,list2[1:])otherwise\mathrm{merge}(\mathit{list1}, \mathit{list2}) = \begin{cases} \mathit{list1}[0] + \mathrm{merge}(\mathit{list1}[1:], \mathit{list2}) & \mathit{list1}[0] < \mathit{list2}[0] \\ \mathit{list2}[0] + \mathrm{merge}(\mathit{list1}, \mathit{list2}[1:]) & \text{otherwise} \end{cases}
  • 即,两个链表头部值较小的一个节点与剩下元素的 merge 操作结果合并

  • 代码实现如下

    class Solution {
    public:
        ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
            if (l1 == nullptr) {
                return l2;
            } else if (l2 == nullptr) {
                return l1;
            } else if (l1->val < l2->val) {
                l1->next = mergeTwoLists(l1->next, l2);
                return l1;
            } else {
                l2->next = mergeTwoLists(l1, l2->next);
                return l2;
            }
        }
    };
这篇文档有帮助吗?

On this page

页面信息