LeetCode算法练习之合并k个有序链表

之前是合并两个,这次的题目是合并k个有序链表,当然也可以用两个的思路去做,比较每个链表中当前的那个值,把最小的加入返回链表中,最后得到的就是合并之后的链表。每一次合并都要检查所有的链表,最终时间复杂度是O(nk)

/**
 * 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) {
        ListNode* res=new ListNode(0);
        ListNode* p1=res;
        int min=0;
        int minpos=0;
        int nullnum=0;
        while(nullnum<lists.size()){
            nullnum=0;
            min=INT_MAX;
            minpos=0;
            for(int i=0;i<lists.size();i++){
                if(lists[i]==nullptr){
                    nullnum++;
                    continue;
                }
                if(lists[i]->val<min){
                    min=lists[i]->val;
                    minpos=i;
                }
            }
            if(nullnum==lists.size()) break;
            ListNode* pdel=lists[minpos];
            cout <<minpos <<endl<< nullnum;
            lists[minpos]=lists[minpos]->next;
            delete pdel;
            p1->next=new ListNode(min);
            p1=p1->next;
        }
        return res->next;
    }
};

但是最终得分很不理想,只有5%。说明还有优化空间,而且最优的时间复杂度应该不是O(nk) 。分析程序我们发现,每一次循环,每一个链表的当前节点都要被检查一遍,如果上一次循环检查了这个节点而它又不是最小值,下一次循环就还要再检查一遍这个节点,导致大量的重复运算。

实际上,每次循环只要找出最小值,而只变化一个元素。这让我想到了一种数据结构:优先队列。有关优先队列的东西,推荐一篇文章:https://mp.weixin.qq.com/s?src=11&timestamp=1584005226&ver=2212&signature=TcfPuKmUrwuqagQzMWvSwRPlN28zoM9E*AYiXQ4Rt2PhIlPP*0P1Wox3E02fQtpE4ALa9F3ZJGoQLO6XN*2mfOSeQtv9-ZIfnvuAa5gwKMYTx3mseEz-ZtCfQzVTqXol&new=1

优先队列实现非常简单,用一个数组就可以手撕。但是STL里面也有优先队列的实现,就不用我们自己去写了。

这个STL优先队列和我想象的非常不一样,写出这段代码的时候,我也查阅了不少关于C++STL中优先队列的资料。解释一下最重要的优先队列声明函数:

priority_queue是一个容器适配器,即把某个底层容器包装起来,好去模拟某种有新功能的数据结构。但这样也就丧失了底层容器的迭代器功能。priority_queue类模板需要提供三个参数:一个成员类型T,一个底层容器类型Container <T>,和一个比较函数Comp。底层容器默认用vector<T>,而比较函数默认用std::less函数,越大的优先级越高。你第一个例子priority_queue< int , vector<int> , greater<int> > que;意思是,这个priority_queue里的数据类型是int,它是用vector<int>作为底层容器实现,并且用系统提供的greater函数作为比较标准。于是越小的数优先级越高。第二个例子,目测不对,类型不一致。

其实这个greater并不是一个比较函数!而是一个类。类中还需要重载函数调用运算符()。至于为什么,我想之后再补充了。。。

总之代码就是这样,放一下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
struct cmp{
    bool operator ()(ListNode* a,ListNode* b){
        return a->val>b->val;
    }
};
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* res=new ListNode(0);
        ListNode* p1=res;
        ListNode* min;
        priority_queue<ListNode*,vector<ListNode*>,cmp> heap;
        for(int i=0;i<lists.size();i++){
            if(lists[i]!=nullptr) heap.push(lists[i]);
        }
        while(!heap.empty()){
            min = heap.top();
            heap.pop();
            if(min!=nullptr&&min->next!=nullptr) heap.push(min->next);
            p1->next=new ListNode(min->val);
            delete min;
            p1=p1->next;
        }
        return res->next;
    }
};

最后,可算是拿到一个可以的分数了,发一下图:

 

顺便吐槽奇葩输入,我就知道会有这一组数据来坑人!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值