合并k排序列表的复杂性

时间:2017-01-10 13:06:51

标签: c++ algorithm

问题是将k个排序列表合并到一个排序列表。 我已经完成了三种解决方案。 首先是二分法,如下:

ListNode* mergeKLists(vector<ListNode*>& lists) 
{
    if(lists.size()==0) return NULL;
    return mergeKLists(lists,0,lists.size()-1);
}

ListNode* mergeKLists(vector<ListNode*>& lists,int start,int end) 
{
    if(start==end) return lists[start];
    ListNode *l1=mergeKLists(lists,start,(start+end)/2);
    ListNode *l2=mergeKLists(lists,(start+end)/2+1,end);
    return merge2Lists(l1,l2);
}

复杂度约为O(nklogk)=(2n k / 2 + 4n k / 4 + ..... nk * k / k)

其次,使用堆,如下所示:

static bool heapComp(ListNode* a, ListNode* b) {
        return a->val > b->val;
}
ListNode* mergeKLists(vector<ListNode*>& lists) { //make_heap
    ListNode head(0);
    ListNode *curNode = &head;
    vector<ListNode*> v;   
    for(int i =0; i<lists.size(); i++){
        if(lists[i]) v.push_back(lists[i]);
    }
    make_heap(v.begin(), v.end(), heapComp); //vector -> heap data strcture

    while(v.size()>0){
        curNode->next=v.front();
        pop_heap(v.begin(), v.end(), heapComp); 
        v.pop_back(); 
        curNode = curNode->next;
        if(curNode->next) {
            v.push_back(curNode->next); 
            push_heap(v.begin(), v.end(), heapComp);
        }
    }
    return head.next;
}

复杂性大约是O(nklogk),因为精炼堆需要logk,我们需要这样做nk次。

第三种方法是我很困惑的。我采用Haffman Three的想法,每次我拿两个最短的列表并进行合并,如下:

struct Node{
  ListNode* node;
  int length;
  Node(ListNode *n,int x){
      node=n;
      length=x;
  }
};
bool compare(Node l1,Node l2)
{
    return l1.length>l2.length;
}
ListNode* mergeKLists(vector<ListNode*>& lists) 
{
        vector<Node> newlists;
        if(lists.empty()) return NULL;
        for(int i=0;i<lists.size();i++)
            newlists.push_back(Node(lists[i],length(lists[i])));
        make_heap(newlists.begin(),newlists.end(),compare);
        while(newlists.size()!=1)
        {
            Node p=newlists.front();
            pop_heap (newlists.begin(),newlists.end(),compare);
            Node q=newlists.front();
            newlists.pop_back();
            pop_heap (newlists.begin(),newlists.end(),compare);
            newlists.pop_back();
            newlists.push_back(Node(merge2Lists(p.node,q.node),p.length+q.length));
            push_heap (newlists.begin(),newlists.end(),compare);
        }
        return newlists[0].node;
}

我认为复杂性是关于O(nklogk + klogk),因为与第一种方法相比,它需要额外的pop_heap O(klogk)。

但是,如果k列表的长度变化很大。很明显,第三种方法优于第一种方法。例如,长度为(20,50,40)的列表,第一个需要进行20 + 50 + 70 + 40 = 180比较,而第三个只需要20 + 40 + 60 + 50 = 170个比较。

这是矛盾吗?

我是否正确计算这些方法的复杂性?

如果列表的长度变化很大,有人能解释这些方法的复杂性吗?

0 个答案:

没有答案