什么是此代码的时间复杂度(来自leetcode)?

时间:2014-01-16 03:52:09

标签: c++ algorithm sorting merge mergesort

问题是“合并k个已排序的链接列表并将其作为一个排序列表返回。”来自leetcode

1

我的解决方案是使用向量来维护每个链表的当前位置,对向量进行排序以获得具有最小值的节点,并将其插入到合并列表的末尾。这是代码:

bool cmp(ListNode *a, ListNode *b) {
return a->val < b->val;
}
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
    ListNode *dummy = new ListNode(-1);
    ListNode *curr = dummy;

    //init
    vector<ListNode*> currNodes;
    for(int i = 0; i < lists.size(); ++i){
        if(lists[i] != NULL){
            currNodes.push_back(lists[i]);
        }
    }

    while(!currNodes.empty()){
        sort(currNodes.begin(), currNodes.end(), cmp);
        curr->next = currNodes[0];
        curr = curr->next;

        if(currNodes[0]->next != NULL){
            currNodes.push_back(currNodes[0]->next);
        }
        currNodes.erase(currNodes.begin());
    }

    return dummy->next;
}
};

由于std :: sort的时间复杂度是nlog(n),我们有(n1 + n2 ... nk)次迭代,因此我认为总时间复杂度为O((n1 + n2 ... + nk) )KLOG(K))。但是在每次迭代时,向量currNodes的大小可能会有所不同,所以我有点困惑。谁能证实这一点?

2。 另外,我在leetcode讨论论坛上看到了另一种使用“合并排序”思想的解决方案。它每次合并两个链表。

public class Solution {
public ListNode mergeKLists(ArrayList<ListNode> lists) {
    // IMPORTANT: Please reset any member data you declared, as
    // the same Solution instance will be reused for each test case.
    if(lists.isEmpty()) return null;
    if(lists.size() == 1) return lists.get(0);
    int k = lists.size();
    int log = (int)(Math.log(k)/Math.log(2));
    log = log < Math.log(k)/Math.log(2)? log+1:log; // take ceiling
    for(int i = 1; i <= log; i++){
        for(int j = 0; j < lists.size(); j=j+(int)Math.pow(2,i)){
            int offset = j+(int)Math.pow(2,i-1);
            lists.set(j, mergeTwoLists(lists.get(j), (offset >= lists.size()? null : lists.get(offset))));
        }
    }
    return lists.get(0);
}


public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    // IMPORTANT: Please reset any member data you declared, as
    // the same Solution instance will be reused for each test case.
    if(l1 == null) return l2;
    if(l2 == null) return l1;
    ListNode head = l1.val > l2.val? l2:l1;
    if(head.equals(l2)){
        l2 = l1;
        l1 = head;
    }
    while(l1.next != null && l2 != null){
        if(l1.next.val > l2.val){
            ListNode tmp = l1.next;
            l1.next = l2;
            l2 = l2.next;
            l1 = l1.next;
            l1.next = tmp;
        }
        else
            l1 = l1.next;
    }
    if(l2 != null){
        l1.next = l2;
    }
    return head;
}
}

我想知道这个解决方案的时间复杂度是多少?由于它每次合并两个链表,因此存在log(n)次迭代。但是链接列表在每次迭代后变得更长(因为它是从两个链表中合并),如何计算每次迭代的时间复杂度然后将它们加在一起?

提前谢谢你:)

2 个答案:

答案 0 :(得分:0)

这是我的解决方案。复杂性是(从k列表中找出1分钟)*(n个节点) 我会说它的O(kn)其中k是列表的数量 最佳解决方案是O(nlogk),请参见此处:How to sort K sorted arrays, with MERGE SORT

但这已经足够leetcode了所以我没有做min-heap

// http://oj.leetcode.com/problems/merge-k-sorted-lists/

public ListNode mergeKLists(ArrayList<ListNode> lists) {
    // Note: The Solution object is instantiated only once and is reused by each test case.
    ListNode cursor = new ListNode(Integer.MAX_VALUE);
    ListNode head = cursor;
    int min = Integer.MAX_VALUE;
    int index = -1;
    while(lists.size()>0){
        for(int i=0; i<lists.size(); i++){//get 1 min
            if(lists.get(i)!=null && lists.get(i).val<min){
                min = lists.get(i).val;
                index = i;
            }
            if(lists.get(i)==null){
                lists.remove(i);
                i--;
            }
        }
        if(index>=0){//put the min in
            cursor.next = lists.get(index);
            cursor = cursor.next;
            lists.set(index,lists.get(index).next);
            if(lists.get(index)==null){
                lists.remove(index);
            }
            min = Integer.MAX_VALUE;
        }
    }
    return head.next;
}

答案 1 :(得分:0)

我认为这个问题有O((n1+n2+n3..nk)logk)解决方案,您可以执行以下操作: -

  
      
  1. 将第一个k元素添加到min heap
  2.   
  3. 删除min元素并添加到新列表
  4.   
  5. 从列表中删除包含min元素的下一个元素并添加到堆中。
  6.   
  7. 继续直到堆为空。
  8.   

更有趣的解决方案: -

使用合并排序,例如合并例程和霍夫曼编码,例如选择: -

假设你有k个列表,每个元素包含n个元素: -

  
      
  1. 将所有列出大小作为密钥的列表添加到min heap
  2.   
  3. 选择两个最小的列表并使用合并排序合并它们,例如例程
  4.   
  5. 将新列表添加到堆中,其大小为键
  6.   
  7. 1到3,直到只剩下一个列表,该列表是您的合并排序列表。
  8.   

如果有k个列表中包含n个元素,那么像合并这样的霍夫曼会给出以下时间复杂度: -

  
      
  1. 从堆中删除两个列表需要O(logk)
  2.   
  3. 合并排序,例如合并需O(n1+n2)
  4.   

算法中的逻辑迭代: -

  
      
  1. 合并n个大小为n的列表中的所有对,取n / 2 *(n + n)= O(n ^ 2)
  2.   
  3. 合并来自n / 2列表的所有对,大小为n / 4 *(2n + 2n)= O(n ^ 2)...完成直到O(logK)迭代。
  4.   

时间复杂度:O(n^2*logk)