Merge k Sorted Lists
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Example:
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6
简单粗暴的写法,依次合并2两个list
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergrlist(ListNode* list1, ListNode* list2)
{
ListNode newNode(-1);
ListNode* pNode = &newNode;
while (list1 && list2)
{
if (list1->val < list2->val)
{
pNode->next = list1;
list1 = list1->next;
}
else
{
pNode->next = list2;
list2 = list2->next;
}
pNode = pNode->next;
}
pNode->next = list1 ? list1 : list2;
return newNode.next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
if (lists.empty())
return NULL;
if (lists.size() == 1)
return lists[0];
ListNode* result = lists[0];
for (int i = 1; i < lists.size(); i++)
{
result = mergrlist(result, lists[i]);
}
return result;
}
};
优化写法:
将每个链表的表头元素取出来,建立一个小顶堆,因为k个链表中都排好序了,因此每次取堆顶的元素就是k个链表中的最小值,可以将其合并到合并链表中,再将这个元素的指针指向的下一个元素也加入到堆中,再调整堆,取出堆顶,合并链表。。。。以此类推,直到堆为空时,链表合并完毕。 链接:这里写链接内容
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
// 使用堆排序,
// 1. 选出每个链表的头来插入小顶堆中,
// 2. 再把堆顶接入合并链表中,
// 3. 被选出的指针后移再加入小顶堆中,回到2
// 4. 最后所有链表都为空时,返回合并链表的头指针
if(lists.empty()) return nullptr;
vector<ListNode* > heap;
// 1. 选出每个链表的头来插入小顶堆中,
for(int i = 0; i != lists.size(); i ++){
if(lists[i]) heap.push_back(lists[i]);
}
makeHeap(heap);
// 2. 再把堆顶接入合并链表中,
ListNode head(-1); // 合并链表的表头
ListNode* p = &head;
while(!heap.empty()){
auto minNode = popHeap(heap);
p->next = minNode; // 接入链表
p = p->next;
// 3. 被选出的指针后移再加入小顶堆中,回到2
auto next = minNode->next;
if(next) pushHeap(heap, next);
}
// 4. 最后所有链表都为空时,返回合并链表的头指针
return head.next;
}
// 建立小顶堆
// 自低向上
void makeHeap(vector<ListNode*> &heap){
// 从最后一个元素的父节点开始建立小顶堆
for(int i = heap.size()/2; i >0 ; i --){
minHeap(heap, i);
}
}
// 调整小顶堆,以第i个元素为根建立小顶堆
//位置从1开始,取元素时记得-1
// 自顶向下
void minHeap(vector<ListNode*> &heap, int i){
int l = i*2;
int r = l+1;
int least(i);
// 算出最小元素的位置
if((l< heap.size()+1) && heap[l-1]->val<heap[i-1]->val ){
// 如果没有超过边界并且左孩子比父亲小,则换
least = l;
}
if(r<heap.size()+1 && heap[r-1]->val<heap[least-1]->val){
// 如果没有超过边界并且右孩子最小,则换
least = r;
}
if(least != i){
swap(heap[i-1], heap[least-1]);
minHeap(heap, least);
}
}
// 在小顶堆中插入一个元素
// 自低向上
void pushHeap(vector<ListNode*> &heap, ListNode* p){
heap.push_back(p);
int child = heap.size();
int parent = child/2;
for(int child = heap.size(),parent = child/2; parent; child--, parent = child/2){
if(heap[child-1]->val < heap[parent-1]->val){
swap(heap[child-1], heap[parent-1]);
}
}
}
// 弹出堆顶
ListNode* popHeap(vector<ListNode*> &heap){
swap(heap[0], heap[heap.size()-1]);
auto p = heap.back();
heap.pop_back();
minHeap(heap, 1);
return p;
}
};
继续优化:
后来想到既然堆每次加入一个元素的时候都要调整堆顶,那么每次把要添加的元素换到堆顶再调整就不用写pushHeap的函数了,当要添加的元素为空时,相当于执行popHeap函数,因此可以简化代码:
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
// 使用堆排序,
// 1. 选出每个链表的头来插入小顶堆中,
// 2. 再把堆顶接入合并链表中,
// 3. 被选出的指针后移再加入小顶堆中,回到2
// 4. 最后所有链表都为空时,返回合并链表的头指针
if(lists.empty()) return nullptr;
vector<ListNode* > heap;
// 1. 选出每个链表的头来插入小顶堆中,
for(int i = 0; i != lists.size(); i ++){
if(lists[i]) heap.push_back(lists[i]);
}
makeHeap(heap);
// 2. 再把堆顶接入合并链表中,
ListNode head(-1); // 合并链表的表头
ListNode* p = &head;
while(!heap.empty()){
auto minNode = heap[0];
p->next = minNode; // 接入链表
p = p->next;
// 3. 被选出的指针后移再加入小顶堆中,回到2
auto next = minNode->next;
if(next) {
heap[0] = next;
}else{
swap(heap[0], heap[heap.size()-1]);
heap.pop_back();
}
minHeap(heap, 1);
}
// 4. 最后所有链表都为空时,返回合并链表的头指针
return head.next;
}
// 建立小顶堆
// 自低向上
void makeHeap(vector<ListNode*> &heap){
// 从最后一个元素的父节点开始建立小顶堆
for(int i = heap.size()/2; i >0 ; i --){
minHeap(heap, i);
}
}
// 小顶堆,以第i个元素为根建立小顶堆
//位置从1开始,取元素时记得-1
// 自顶向下
void minHeap(vector<ListNode*> &heap, int i){
int l = i*2;
int r = l+1;
int least(i);
// 算出最小元素的位置
if((l< heap.size()+1) && heap[l-1]->val<heap[i-1]->val ){
// 如果没有超过边界并且左孩子比父亲小,则换
least = l;
}
if(r<heap.size()+1 && heap[r-1]->val<heap[least-1]->val){
// 如果没有超过边界并且右孩子最小,则换
least = r;
}
if(least != i){
swap(heap[i-1], heap[least-1]);
minHeap(heap, least);
}
}
};
整体而言,复杂度从O(nk²)降到O(nk²)。