题目:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
struct compare{
bool operator()(const ListNode* p1, const ListNode* p2){
return p1->val > p2->val;//相当于great,小顶堆
}
};//类和结构体定义要以分号结束!!
class Solution {
public://用STL优先队列实现小顶堆
ListNode* mergeKLists(vector<ListNode*>& lists) {
//两种方案:将问题转化为合并两个list(O(k*n^2));或者每次都选出n个list中最小的(小顶堆)(k*nlogk),其中k为链表个数,n为链表平均长度
//STL的优先队列是用堆实现的,默认情况下是大顶堆。对于非内置类型,必须重载operator()
if (lists.empty()) return NULL;
priority_queue<ListNode* , vector<ListNode* >, compare> q;
for (int i = 0; i < lists.size(); ++i){//建堆
if (lists[i]) q.push(lists[i]);
}
if (q.empty()) return NULL;
ListNode *head = q.top();
q.pop();
ListNode *p = head;
ListNode *pp = p->next;
if (pp) q.push(pp);
while (!q.empty()){
p->next = q.top();//将堆顶节点连入新链表
p = p->next;//p指向新链表最后一个节点
pp = p->next;//取得堆顶下一个节点
q.pop();//弹出堆顶节点
if (pp) q.push(pp);//如果新节点的不为空,则将其入堆
}
return head;
}
};
以下版本是用数组自己建的最小堆
/**
* 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) {
//每次都选出n个list中最小的(小顶堆)
//时间复杂度(k*nlogk,其中k为链表个数,n为链表平均长度
//用数组实现最小堆
if(lists.empty()) return NULL;
int n = lists.size();//n表示链表个数
ListNode **p = new ListNode *[n];//p[i]指向第i个链表的当前节点
int *heap = new int[n];//heap[0]是堆顶元素,它表示堆顶来自哪个链表。堆在数组中的结构是i表示父节点,2*i+1为i的左孩子,2*i+2位i的右孩子。注意不要越界
//初始化p和heap
for(int i = 0; i < n; ++i){
p[i] = lists[i];
heap[i] = i;
}
//建堆
for(int i = n/2; i >= 0; --i){//对所有的非叶节点调整
minHeapAdjust(p, heap, i, n);
}
//取得堆顶元素
ListNode *head = p[heap[0]];//head指向新链表第一个节点
ListNode *pNew = head;//pNew指向当前新链表的最后一个节点
while(p[heap[0]]){
p[heap[0]] = p[heap[0]]->next;//原堆顶链表中的下一个节点放入堆顶位置
minHeapAdjust(p, heap, 0, n);//调整堆
pNew->next = p[heap[0]];//堆顶元素连入新链表
pNew = pNew->next;//pNew指向新链表的最后一个节点
}
return head;
}
private:
void minHeapAdjust(ListNode** p, int* heap, int pos, int n){//如果到达链表结尾该怎么处理?val置为INT_MAX
int child = pos*2 + 1;//需要调整节点的左孩子
int origin = heap[pos];//heap[pos]表示在堆中处于pos位置的节点来自哪个链表
while(child < n){//从上至下调整,注意不要越界
//找到左右孩子中的较小者
int val1 = p[heap[child]] ? p[heap[child]]->val : INT_MAX;
int val2 = INT_MAX;
if(child+1 < n){//!!先判断右孩子是否越界
val2 = p[heap[child+1]] ? p[heap[child+1]]->val : INT_MAX;
}
int valChild = val1;
if(val2 < val1){
valChild = val2;
++child;
}
//是否需要调整(!!是根据当前位置的左右孩子是否小于origin而不是pos)
int valPos = p[origin] ? p[origin]->val : INT_MAX;
if(valPos <= valChild) break;//找到了合适的位置,不需要调整
//需要调整
heap[pos] = heap[child];//左右孩子中较小者调整到pos
pos = child;//继续向下调整,pos是origin可能的合适位置,一旦找到,就将origin放入pos,所以每次都要更新
child = child*2 + 1;
}
heap[pos] = origin;//将原值放入合适位置
}
};