23.合并K个排序链表(Merge k Sorted Lists)

题目描述:

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

解题思路:

  合并k个排序链表,方法有很多。比如最开始相当的就是先合并前两个链表,然后依次合并接下来的链表,不过这样写时间复杂度很高,因为有很多重复遍历的操作。接着会想到先全部取到一个容器中,然后再进行后续的操作。接下来我们讨论全部取到一个容器中的思路。

  很容易想到的是,我们直接依次遍历k个链表的所有结点,然后全部直接放在vector或是别的什么容器中,再对容器中的元素进行排序,最后导出元素为一个链表。这样假设有n个元素,则插入的时间复杂度是O(n),排序是O(nlgn),导出是O(n),总时间复杂的数量级在nlgn,不过系数会很大。

  上面的那种想法显然浪费了k个链表是已排序的特点,那如何利用这个条件呢?我想到的是,每次比较k的链表的第一个元素,取出其中最小的那个元素,作为返回链表的表头,然后被取出元素的指针向下移动。再次和另外两个结点比较,取出最小的那个,加在返回链表的尾部,依次往复。这样最坏情况下每插入一个元素都要比较k次,假设有一共有n个元素,则时间复杂度为O(k*n)。

  可见用哪种方法取决于k和n的大小,但是题目所给出的链表并不能直接返回长度,所以为了方便起见,我直接把所有元素都插入到multiset中,然后再依次导出。

  代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.empty())
            return nullptr;
        multiset<int> ret;
        for (auto l : lists) {
            auto p = ret.begin();
            while (l != nullptr) {
                p = ret.insert(p, l->val);
                l = l->next;
            }
        }
        if (ret.empty())
            return nullptr;
        auto p = ret.begin();
        ListNode *start = new ListNode(*p);
        ListNode *end = start;
        for (++p; p != ret.end(); ++p) {
            end->next = new ListNode(*p);
            end = end->next;
        }
        return start;
    }
};

 

posted @ 2020-04-15 09:56  简讯  阅读(165)  评论(0编辑  收藏  举报