21. 合并两个有序链表

发布时间 2023-10-20 22:29:47作者: DawnTraveler

1.题目介绍

2.题解

一定注意题目给的两个链表可能为空,需要提前进行判断

2.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 *list1, ListNode *list2) {
        ListNode *p1 = nullptr, *p2 = nullptr, *temp = nullptr, *pre = nullptr;
        bool flag = false;
        if (list1 == nullptr) return list2;
        if (list2 == nullptr) return list1;
        if (list1->val <= list2->val) {
            p1 = list1;
            p2 = list2;
        }
        else {
            p1 = list2;
            p2 = list1;
            flag = true;
        }
        while (p1 != nullptr && p2 != nullptr) {
            while (p1->val <= p2->val) {
                pre = p1;
                p1 = p1->next;
                if (p1 == nullptr) {
                    pre->next = p2;
                    if (!flag) return list1; else return list2;
                }
            }
            p1 = pre;
            temp = p2->next;
            p2->next = p1->next;
            p1->next = p2;
            p2 = temp;
        }
        if (!flag) return list1; else return list2;
    }
};

2.2 优化

虚拟头结点

在上面的程序中,进行了一次大小判断决定是以list1还是list2为头结点进行,为此还要设置标志位flag为后面return判断提供依据。在最后某个链表遍历结束后进行的判断也过于分散不利于阅读。
但是只要使用一个虚拟头结点便可解决这些问题:

    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if (list1 == nullptr) return list2;
        if (list2 == nullptr) return list1;

        /* 虚拟头结点 */
        ListNode dummy(0);
        ListNode* current = &dummy;

        while (list1 != nullptr && list2 != nullptr) {
            if (list1->val <= list2->val) {
                current->next = list1;
                list1 = list1->next;
            } else {
                current->next = list2;
                list2 = list2->next;
            }
            current = current->next;
        }

        if (list1 != nullptr) {
            current->next = list1;
        } else {
            current->next = list2;
        }

        return dummy.next;
    }

完整程序实例

#include <iostream>

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 dummy(0);
        ListNode* current = &dummy;

        while (list1 != nullptr && list2 != nullptr) {
            if (list1->val <= list2->val) {
                current->next = list1;
                list1 = list1->next;
            } else {
                current->next = list2;
                list2 = list2->next;
            }
            current = current->next;
        }

        if (list1 != nullptr) {
            current->next = list1;
        } else {
            current->next = list2;
        }

        return dummy.next;
    }

    void Print_LinkList(ListNode* l) {
        using namespace std;
        cout << "该链表为:";
        while (l != nullptr) {
            cout << l->val << ' ';
            l = l->next;
        }
        cout << endl;
    }
};

int main() {
    Solution solution;
    ListNode* head1 = new ListNode(1);
    head1->next = new ListNode(2);
    head1->next->next = new ListNode(4);
    head1->next->next->next = new ListNode(8);
    solution.Print_LinkList(head1);
    ListNode* head2 = new ListNode(1);
    head2->next = new ListNode(3);
    head2->next->next = new ListNode(4);
    solution.Print_LinkList(head2);
    ListNode* l = solution.mergeTwoLists(head1, head2);
    solution.Print_LinkList(l);

    // 释放内存,防止内存泄漏
    while (l != nullptr) {
        ListNode* temp = l;
        l = l->next;
        delete temp;
    }

    return 0;
}

2.3 使用递归

思路算法

  • 终止条件:当两个链表都为空时,表示我们对链表已合并完成。
  • 如何递归:我们判断 l1 和 l2 头结点哪个更小,然后较小结点的 next 指针指向其余结点的合并结果。(调用递归)