LeetCode 23 Merge k Sorted Lists
题目分析
https://leetcode.com/problems/merge-k-sorted-lists/ k 个链表,设 n 为每个链表的元素个数。
直接法
第一次的时候排序各个链表的首节点,之后只取最小值,然后新的值未必是最小的,冒泡到其目标位置。
我们需要处理 nk 个节点,每个节点的冒泡如果用二分搜索寻找合适位置的话复杂度 O(logk),插入本身则要 O(k), 最终的时间复杂度为 O(nk^2)。
class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        // remove all nullptr in the lists
        auto it = lists.begin();
        while (it != lists.end()) {
            if (*it == nullptr) {
                it = lists.erase(it);
            } else {
                ++it;
            }
        }
        if (lists.size() == 0) return nullptr;
        sort(begin(lists), end(lists), [](const auto &lhs, const auto &rhs) {
            return lhs->val < rhs->val;
        });
        ListNode head;
        ListNode *tail = &head;
        while (true) {
            tail->next = lists[0];
            tail = tail->next;
            lists[0] = lists[0]->next;
            if (lists[0] == nullptr) {
                lists.erase(lists.begin());
                if (lists.empty()) break;
            }
            // Find a proper position for cur.
            int i = 1;
            for (; i < lists.size(); i++) {
                if (lists[0]->val <= lists[i]->val) {
                    break;
                }
            }
            lists.insert(lists.begin() + i, lists[0]);
            lists.erase(lists.begin());
            // move(lists, 0, i-1);
        }
        return head.next;
    }
};
直接法(维护两个排序数组)
维护两个排序数组,第一次的时候排序各个链表的首节点,作为第一个排序数组; 之后只取最小值,然后新的值放到两个排序数组中元素个数较小的那个。
也就是说,每次新的值我们需要跟两个数组的最小值进行比较:
- 其是最小的,那直接拼接它就好了。
- 其不是最小的,返回两个数组的末尾值中较小的那个,然后把新值插入到排序数组的合适位置。
同样需要处理 nk 个节点,两个数组的平均长度为 k,寻找合适位置的话复杂度 O(logk),插入本身则要 O(k), 最终的时间复杂度也为 O(nk^2)。
嗯,相比上一个方法并不划算啊喂!
递归法
两两合并列表,最后合并成一个已排序数组。 k 个链表,则我们需要操作 k 次,每次的复杂度为 O(n),但是这里的 n 会越来越大,所以最终的 时间复杂度不是 O(kn),而是 O(kn^2)。
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.size() == 0) return nullptr;
        vector<ListNode*> temp;
        while (lists.size() > 1) {
            for (int i = 0; i < lists.size(); i += 2) {
                if (i + 1 < lists.size()) {
                    temp.push_back(mergeTwoLists(lists[i], lists[i+1]));
                } else {
                    temp.push_back(lists[i]);
                }
            }
            lists = temp;
            temp.clear();
        }
        return lists[0];
    }
    
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode head;
        ListNode* c = &head;
        while (list1 && list2) {
            if (list1->val < list2->val) {
                c->next = list1;
                list1 = list1->next;
            } else {
                c->next = list2;
                list2 = list2->next;
            }
            c = c->next;
        }
        c->next = list1 ? list1 : list2;
        return head.next;
    }
};
优先级队列
https://labuladong.gitee.io/algo/2/17/16/#合并-k-个有序链表
优先级队列适合处理这种只需要极值的情况。
每个元素都要入和出,一共 mk 个元素,堆的尺寸为 k,则时间复杂度为 O(mklogk)
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        auto comp = [](ListNode* a, ListNode* b) {
          return a->val > b->val;  
        };
        priority_queue<ListNode*, vector<ListNode*>, decltype(comp)> pq(comp);
        for (auto list : lists) {
            if (list) {
                pq.push(list);
            }
        }
        ListNode head;
        auto c = &head;
        while (!pq.empty()) {
            c->next = pq.top();
            pq.pop();
            if (c->next->next) {
                pq.push(c->next->next);
            }
            c = c->next;
        }
        return head.next;
    }
};