目录
牛客_NC51合并k个已排序的链表
描述:
合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
数据范围:节点总数 0≤n≤5000,每个节点的val满足 ∣val∣<=1000
要求:时间复杂度 O(nlogn)
题目解析
如果是两个有序链表合并,我们可能会利用归并排序合并阶段的思想:准备双指针分别放在两个链表头,每次取出较小的一个元素加入新的大链表,将其指针后移,继续比较,这样我们出去的都是最小的元素,自然就完成了排序。
其实这道题我们也可以两两比较,只要遍历链表数组,取出开头的两个链表,按照上述思路合并,然后新链表再与后一个继续合并,如此循环,知道全部合并完成。但是,这样太浪费时间了。既然都是归并排序的思想了,那可不可以直接归并的分治来做,而不是顺序遍历合并链表呢?答案是可以的!
如果非要按照归并排序的合并思路,双指针不够用,我们可以直接准备kk个指针,每次比较得出kk个数字中的最小值。为了快速比较k个数字得到最小值,可以利用Java提供的PriorityQueue或者C++SLT提供的优先队列实现,它是一种参照堆排序的容器,容器中的元素是有序的,如果是小顶堆,顶部元素就是最小的,每次可以直接取出最小的元素。也就是说每次该容器中有k个元素,我们可以直接拿出最小的元素,再插入下一个元素,相当于每次都是链表的k个指针在比较大小,只移动最小元素的指针。
C++代码
class Solution {
struct cmp
{
bool operator()(ListNode* l1, ListNode* l2)
{
return l1->val > l2->val;
}
};
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
priority_queue<ListNode*, vector<ListNode*>, cmp> heap; // ⼩根堆
for (auto head : lists)
{
if (head != nullptr)
heap.push(head);
}
ListNode* ret = new ListNode(0);
ListNode* prev = ret;
while (heap.size())
{
ListNode* t = heap.top();
heap.pop();
prev = prev->next = t;
if (t->next != nullptr)
{
heap.push(t->next);
}
}
ListNode* tmp = ret->next;
free(ret);
return tmp;
}
};
Java代码
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution
{
public ListNode mergeKLists (ArrayList<ListNode> lists)
{
PriorityQueue<ListNode> heap = new PriorityQueue<>((l1, l2) -> {
return l1.val - l2.val;
});
for(int i = 0; i < lists.size(); i++)
{
if(lists.get(i) != null)
{
heap.offer(lists.get(i));
}
}
ListNode ret = new ListNode(0);
ListNode prev = ret;
while(!heap.isEmpty())
{
ListNode t = heap.poll();
prev = prev.next = t;
if(t.next != null)
{
heap.offer(t.next);
}
}
return ret.next;
}
}