LC.23 | 合并K个升序链表 | 链表 | 暴力/优先队列/分治

输入:一个链表数组 lists,其中每个链表都已经按升序排列。

要求:请你将所有链表合并为一个升序链表,并返回合并后的链表头结点。

输出:合并后的升序链表头结点。


思路1:暴力排序法(收集 + 排序 + 尾插法)

  1. 遍历所有链表节点,把所有数值放入一个数组 vals

  2. vals 进行排序;

  3. 使用尾插法重新构建一个新的升序链表;

  4. 返回新链表的头结点。

复杂度:

  • 时间复杂度:O(N log N)

  • 空间复杂度:O(N)

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        vector<int> vals;

        // 1. 收集所有节点的值
        for (auto head : lists) {
            while (head) {
                vals.push_back(head->val);
                head = head->next;
            }
        }

        // 2. 排序
        sort(vals.begin(), vals.end());

        // 3. 尾插法建表
        ListNode dummy(0);
        ListNode* tail = &dummy;
        for (int v : vals) {
            tail->next = new ListNode(v);
            tail = tail->next;
        }

        return dummy.next;
    }
};

思路2:优先队列法

利用priority_queue,实现一个自动选择当前最小节点的过程:

  1. 将所有链表的头节点放入小顶堆;

  2. 每次取出堆顶最小节点,接到新链表尾部;

  3. 若该节点有 next,则将 next 入堆;

  4. 重复直到堆为空。

复杂度:

  • 时间复杂度:O(N log K)

  • 空间复杂度:O(K)

class Solution {
public:
    struct Compare {
        bool operator()(ListNode* a, ListNode* b) {
            return a->val > b->val;  // 小顶堆
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<ListNode*, vector<ListNode*>, Compare> pq;

        // 1. 所有头节点入堆
        for (auto node : lists) {
            if (node) pq.push(node);
        }

        // 2. 虚拟头节点 + 尾插法
        ListNode dummy(0);
        ListNode* tail = &dummy;

        while (!pq.empty()) {
            ListNode* smallest = pq.top();
            pq.pop();

            tail->next = smallest;
            tail = tail->next;

            if (smallest->next) pq.push(smallest->next);
        }

        return dummy.next;
    }
};

思路3:分治归并法(递归两两合并)

采用分治思想,将 K 个链表不断二分为左右两部分,递归合并:

  1. 若只有一个链表则直接返回;

  2. 否则递归合并左右两半;

  3. 最后再调用 LC.21 的“合并两个有序链表”来完成一次归并。

相当于对链表数组执行归并排序,每层合并 O(N),共有 logK 层。

复杂度:

  • 时间复杂度:O(N log K)

  • 空间复杂度:O(log K)

class Solution {
public:
    // 合并两个有序链表(复用 LC.21 思路)
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode dummy(-1);
        ListNode* tail = &dummy;

        while (l1 && l2) {
            if (l1->val <= l2->val) {
                tail->next = l1;
                l1 = l1->next;
            } else {
                tail->next = l2;
                l2 = l2->next;
            }
            tail = tail->next;
        }

        tail->next = l1 ? l1 : l2;
        return dummy.next;
    }

    // 分治递归合并
    ListNode* merge(vector<ListNode*>& lists, int left, int right) {
        if (left > right) return nullptr;
        if (left == right) return lists[left];

        int mid = (left + right) / 2;
        ListNode* l1 = merge(lists, left, mid);
        ListNode* l2 = merge(lists, mid + 1, right);
        return mergeTwoLists(l1, l2);
    }

    // 主函数
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.empty()) return nullptr;
        return merge(lists, 0, lists.size() - 1);
    }
};

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值