LeetCode 23: Merge k Sorted Lists

高效合并多个已排序链表
本文探讨了如何高效地合并k个已排序的链表为一个有序链表,包括了时间复杂度分析、多种解题思路及实现代码。

Merge k Sorted Lists

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

解题思路

思路一:首先取第一个链表为中间结果。然后使用 merge2Lists 方法将中间结果链表和数组中下一个链表合并为一条新的链表作为中间结果,以此类推。假设每条链表平均有n个元素,则时间复杂度是O((k1)n+(k2)n++2n+n)=O(nk²)。代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
private:
    // 合并两个链表
    ListNode * merge2Lists(ListNode *l1, ListNode *l2) {
        if (l1 == NULL) return l2;
        if (l2 == NULL) return l1;

        ListNode *head = new ListNode(0), *tail = head;

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

        if (l1) tail->next = l1;

        if (l2) tail->next = l2;

        tail = head;
        head = head->next;
        delete tail;

        return head;
    }
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int len = lists.size();

        if (len < 1) return NULL;

        // 先设当前结果为第一个链表
        ListNode *result = lists[0];

        // 将当前结果与 lists[i] 合并,并更新当前的合并结果 
        for (int i = 1; i < len; ++i) {
            result = merge2Lists(result, lists[i]);
        }

        return result;
    }
};

思路二:采用分治法。先把数组中 k 个链表分成前、后两部分;然后调用 mergeKLists 函数递归对这两部分进行处理;最后调用 merge2Lists 将前半部分和后半部分的结果进行合并。递归结束条件是 k==0 || k == 1。

假设总共有 k 个 list,每个 list 的最大长度是 n,那么运行时间满足递推式T(k)=2T(k/2)+O(nk),因此总的时间复杂度是O(nklogk)

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
private:
    // 合并两个链表
    ListNode * merge2Lists(ListNode *l1, ListNode *l2) {
        if (l1 == NULL) return l2;
        if (l2 == NULL) return l1;

        ListNode *head = new ListNode(0), *tail = head;

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

        if (l1) tail->next = l1;

        if (l2) tail->next = l2;

        tail = head;
        head = head->next;
        delete tail;

        return head;
    }
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int len = lists.size();

        if (len == 0) return NULL;
        if (len == 1) return lists[0];

        // 分治法:先递归的对数组的前半部分和后半部分进行mergeKLists
        vector<ListNode *> leftLists(lists.begin(), lists.begin() + (len >> 1));
        vector<ListNode *> rightLists(lists.begin() + (len >> 1), lists.end());
        ListNode *leftResult = mergeKLists(leftLists);
        ListNode *rightResult = mergeKLists(rightLists);

        // 最后将调用 merge2Lists 合并前半部分和后半部分 mergeKLists 返回的结果
        return merge2Lists(leftResult, rightResult);
    }
};

思路三:另一个思路是将 k 个链表中具有最小值的表头结点加入到结果链表中,并将相应链表的表头指针移到下一个结点。重复该操作,直到说有元素都加入到结果链表中。

为了快速找到具有最小值的表头结点,我们维护一个大小为 k 的最小堆,每次取走堆顶的结点加入到结果链表中,并将该结点的下一个结点放入堆中。假设每个 list 的最大长度是 n,那么该方法的时间复杂度为O(nklogk)

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

struct cmp {
    bool operator() (ListNode *a, ListNode *b) {
        return a->val > b->val;
    }
}; 

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int len = lists.size();
        // 建立大小为 len 的最小堆
        priority_queue<ListNode *, vector<ListNode *>, cmp> minHeap;
        for (int i = 0; i < len; ++i) {
            if (lists[i] != NULL) {
                minHeap.push(lists[i]);
            }
        }

        ListNode *head = new ListNode(-1), *tail = head;
        while (!minHeap.empty()) {
            ListNode *top = minHeap.top();
            minHeap.pop();
            tail->next = top;
            tail = top;

            // Note
            if (top->next) {
                minHeap.push(top->next);
            }
        }

        tail = head;
        head = head->next;
        delete tail;

        return head;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值