[leetcode][堆] Merge k Sorted Lists

题目:

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;//将原值放入合适位置
    }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值