Merge k Sorted Lists leetcode

来源:互联网 发布:工厂巡检数据怎么分析 编辑:程序博客网 时间:2024/06/08 08:20

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

这道题目在分布式系统中非常常见,来自不同client的sorted list要在central server上面merge起来。这个题目一般有两种做法,下面一一介绍并且分析复杂度。 第一种做法比较容易想到,就是有点类似于MergeSort的思路,就是分治法,不了解MergeSort的朋友,请参见归并排序-维基百科,是一个比较经典的O(nlogn)的排序算法,还是比较重要的。思路是先分成两个子任务,然后递归求子任务,最后回溯回来。这个题目也是这样,先把k个list分成两半,然后继续划分,知道剩下两个list就合并起来,合并时会用到Merge Two Sorted Lists这道题,

算法1:leetcode超时

最傻的做法就是先1、2合并,12结果和3合并,123结果和4合并,…,123..k-1结果和k合并,我们计算一下复杂度。

1、2合并,遍历2n个节点

12结果和3合并,遍历3n个节点

123结果和4合并,遍历4n个节点

123..k-1结果和k合并,遍历kn个节点

总共遍历的节点数目为n(2+3+…+k) = n*(k^2+k-2)/2, 因此时间复杂度是O(n*(k^2+k-2)/2) = O(nk^2),代码如下:

class Solution {public:    ListNode *mergeKLists(vector<ListNode *> &lists) {        if(lists.size() == 0)            return nullptr;        ListNode* res = lists[0];        for(int i=1;i<lists.size();i++) {            res = mergeLists(res,lists[i]);        }        return res;    }    ListNode* mergeLists(ListNode* a,ListNode* b) {        if(a == nullptr)            return b;        if(b == nullptr)            return a;        ListNode* helper = new ListNode(0);        ListNode* pNodeA = a;        ListNode* pNodeB = b;        ListNode* last = helper;        while(pNodeA != nullptr && pNodeB != nullptr) {            if(pNodeA->val <= pNodeB->val) {                last->next = pNodeA;                pNodeA = pNodeA->next;            } else {                last->next = pNodeB;                pNodeB = pNodeB->next;            }            last = last->next;        }        if(pNodeA != nullptr)            last->next = pNodeA;        if(pNodeB != nullptr)            last->next = pNodeB;        return helper->next;    }};
算法2:利用分治的思想把合并k个链表分成两个合并k/2个链表的任务,一直划分,知道任务中只剩一个链表或者两个链表。可以很简单的用递归来实现。因此算法复杂度为T(k) = 2T(k/2) + O(nk),很简单可以推导得到算法复杂度为O(nklogk)

    ListNode *mergeKLists(vector<ListNode *> &lists) {        if(lists.size() == 0)            return nullptr;        return helper(lists,0,lists.size()-1);    }    ListNode* helper(vector<ListNode *> &lists,int l,int r) {        if(l<r) {            int m = l + (r-l)/2;            return mergeLists(helper(lists,l,m),helper(lists,m+1,r));        }        return lists[l];    }

下面是非递归的代码非递归的思想是(以四个链表为例):              

1、3合并,合并结果放到1的位置

2、4合并,合并结果放到2的位置

再把1、2合并(相当于原来的13 和 24合并)

    ListNode *mergeKLists(vector<ListNode *> &lists) {        int n = lists.size();        if(n == 0)return NULL;        while(n >1)        {            int k = (n+1)/2;            for(int i = 0; i < n/2; i++)                lists[i] = merge2list(lists[i], lists[i + k]);            n = k;        }        return lists[0];    }     

算法3:维护一个k个大小的最小堆,初始化堆中元素为每个链表的头结点,每次从堆中选择最小的元素加入到结果链表,再选择该最小元素所在链表的下一个节点加入到堆中。这样当堆为空时,所有链表的节点都已经加入了结果链表。元素加入堆中的复杂度为O(longk),总共有kn个元素加入堆中,因此,复杂度也和算法2一样是O(nklogk)

class Solution {private:struct cmp{    bool operator ()(const ListNode *a, const ListNode *b)    {            return a->val > b->val;    }};public:    ListNode *mergeKLists(vector<ListNode *> &lists) {        int n = lists.size();        if(n == 0)return NULL;        ListNode node(0), *res = &node;        priority_queue<ListNode*, vector<ListNode*>, cmp> que;        for(int i = 0; i < n; i++)            if(lists[i])                que.push(lists[i]);        while(!que.empty())        {            ListNode * p = que.top();            que.pop();            res->next = p;            res = p;                         if(p->next)                que.push(p->next);        }        return node.next;    }};




0 0