又是一道考察频率很高的题目,我至少看到有 5 个人碰到了这题。
题目链接:
合并K个排序链表 - 力扣(LeetCode)leetcode-cn.com题目描述:
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
解题思路:
题目很简短,就是合并多个有序链表,有一个很流氓的做法就是,先把所有链表遍历一遍,把元素存到数组中去,然后对数组排序,再按顺序生成一个新的链表,时间复杂度是
,因为其中最耗时的排序操作是
,N 是节点的总数目,代码如下:
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
vector<int> vec;
for(ListNode* head : lists){
while(head){
vec.push_back(head->val);
head = head->next;
}
}
ListNode* dump = new ListNode(0);
ListNode* p = dump;
sort(vec.begin(), vec.end());
for(int num : vec){
p->next = new ListNode(num);
p = p->next;
}
p->next = NULL;
return dump->next;
}
};
我们看有没有别的做法,其实既然是要排序 k 个链表,我们可以先从两个开始嘛,排完了不就只剩 k-1 个了么?再排两个,就剩 k-2 个,就这么循环往复,直到只剩最后一个链表,任务就完成了。
当然实现的时候,为了保证消耗的时间尽可能短,我们需要开始的时候排短的,后面再排合并之后相对较长的,但是这种方法也是比较耗时的,因为两两合并的过程中链表越来越长,很多元素其实是被比较了很多次,时间复杂度是
,其中 k 是链表的数目,代码如下:
s Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.size()==0) return NULL;
while(lists.size() > 1){
lists.push_back(mergeTwoLists(lists[0], lists[1]));
lists.erase(lists.begin());
lists.erase(lists.begin());
}
return lists[0];
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2){
ListNode* dump = new ListNode(0);
ListNode* p = dump;
while(l1 && l2){
if(l1->val <= l2->val){
p->next = l1;
l1 = l1->next;
}
else{
p->next = l2;
l2 = l2->next;
}
p = p->next;
}
if(!l1) p->next = l2;
else p->next = l1;
return dump->next;
}
};
这两种方法虽然耗时都比较高,但是毕竟笔试的时候好想,如果没有其他思路可以写。但是为了更高的效率我们还是说一下更快的方法,本题还可以用分治法求解,这个方法沿用了上面逐一两两合并链表的解法,但是进行了较大的优化,我们不需要对大部分节点重复遍历多次。
- 将 k 个链表配对并将同一对中的链表合并。
- 第一轮合并以后,k 个链表被合并成了 k/2 个链表,平均长度为 2N/k,然后是 k/4 个链表,k/8 个链表等等。
- 重复这一过程,直到我们得到了最终的有序链表。
因此,我们在每一次配对合并的过程中都会遍历几乎全部 N 个节点,并重复这一过程
次,时间复杂度为
。
代码如下:
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.size() == 0) return NULL;
return merge(lists, 0, lists.size() - 1);
}
private:
ListNode* merge(vector<ListNode*>& lists, int left, int right) {
if (left == right) return lists[left];
int mid = left + (right - left) / 2;
ListNode* l1 = merge(lists, left, mid);
ListNode* l2 = merge(lists, mid + 1, right);
return mergeTwoLists(l1, l2);
}
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
if (l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
else {
l2->next = mergeTwoLists(l1,l2->next);
return l2;
}
}
};
还有使用优先队列的方法,也有很高的效率,有兴趣的同学可以试试。
如果有任何疑问,欢迎提出。如果有更好的解法,也欢迎告知。