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; } };