Leetcode之算法专题《Merge k Sorted Lists》

题目内容如下(链接:https://leetcode.com/problems/merge-k-sorted-lists/description/

中文说明:把k个有序的链表合并成1个有序的链表输出

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

1. 看到这道题,我的第1个想法就是每次把所有输入链表最前面未排序的元素进行大小比较,把最小的元素从所在输入链表移出到结果链表尾部,循环直至所有输入链表都为空,代码如下:

/**
 * 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) {
        int iListSize = lists.size();
        int flag = -1;
        for(int i=0;i<iListSize; ++i)
        {
            if(lists[i] && (flag < 0 || lists[i]->val < lists[flag]->val))
            {
                flag = i;
            }
        }
        if(flag<0) return NULL;
        ListNode* temp = lists[flag];
        lists[flag] = lists[flag]->next; 
        temp->next = mergeKLists(lists);
        return temp;
    }
};

结果Accepted了,花时416ms,直打败了5.6%的人,很尴尬

2. 仔细想想,每次都要把所有输入链表的第一个元素进行比较,这样是不是比较的次数太多了,还不如先取第1个输入链表和第2个输入链表进行归并,把结果链表再与第3个输入链表归并,......,直到所有链表归并完,这样好像比较的次数会比之前的方法少,于是敲了以下代码:

class Solution {
public:
    ListNode* merge2Lists(ListNode* list1, ListNode* list2) {
        ListNode* head = NULL;
        ListNode* temp = NULL;
        if(!list1) return list2;
        if(!list2) return list1;
        
        if(list1->val < list2->val) { 
            head = temp = list1;
            list1 = list1->next;
        }
        else {
            head = temp = list2;
            list2 = list2->next;
        }
        while(list1 && list2) {
            if(list1->val < list2->val) {
                temp->next = list1;
                list1 = list1->next;
                temp = temp->next;
            }
            else{
                temp->next = list2;
                list2 = list2->next;
                temp = temp->next;
            }
        }
        if(!list1) {
            temp->next = list2;
        }
        else{
            temp->next = list1;
        }
        return head;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int iListSize = lists.size();
        if(iListSize == 0) return NULL;
        ListNode* list1 = lists[0];
        for(int i=1; i<iListSize; ++i)
        {
            list1 = merge2Lists(list1, lists[i]);
        }
        return list1;
    }
};

结果跑了128ms,打败了22.5%的人。

3. 很明显,第2种解法并没有本质上的改进,一定还有更好的解法。突然想到每次对所有输入链表最前面未排序的元素进行大小比较的过程完全可以用堆进行优化,因为每次其实只是移出1个链表的首元素,搞个最小堆维护不就好了,STL的优先队列刚好可以拿来用,于是把代码重写了一下:

class Solution {
private:
    struct Compare {
        bool operator()(ListNode* const& n1, ListNode* const& n2) {
            return n1->val > n2->val;
        }
    };
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int iListSize = lists.size();
        priority_queue<ListNode*, vector<ListNode*>, Compare> q;
        for(int i=0;i<iListSize; ++i)
        {
            if(lists[i])q.push(lists[i]);
        }
        ListNode head(0);
        ListNode* temp = &head;
        while(!q.empty())
        {
            temp->next = q.top();
            temp = temp->next;
            q.pop();
            if(temp->next) q.push(temp->next);
        }
        return head.next;
    }
};

好了,这次执行了24ms,打败了57.5%的人,看了下第一名是16ms,跟24ms并没有量级差,所以,这差不多就是最优解咯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值