这段代码的时间复杂度是多少(来自 leetcode)?

Posted

技术标签:

【中文标题】这段代码的时间复杂度是多少(来自 leetcode)?【英文标题】:what is time complexity of this code (from leetcode)? 【发布时间】:2014-01-16 03:52:09 【问题描述】:

问题是“合并 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) 次迭代。但是链表在每次迭代后都会变长(因为它是由两个链表合并而成的),如何计算每次迭代的时间复杂度,然后将它们相加?

提前谢谢你:)

【问题讨论】:

【参考方案1】:

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

但这对于 leetcode 来说已经足够了,所以我没有做最小堆

//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;

【讨论】:

【参考方案2】:

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

    将第一个 k 元素添加到最小堆 删除 min 元素并添加到新列表中 从列表中删除包含 min 元素的下一个元素并添加到堆中。 继续直到堆为空。

更有趣的解决方案:-

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

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

    将所有具有大小的列表作为键添加到最小堆中 选择两个最小的列表并使用合并排序例程将它们合并 将新列表添加到堆中,并将其大小作为键 1 到 3 直到只剩下一个列表,该列表就是您的合并排序列表。

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

    从堆中删除两个列表需要O(logk) 合并排序就像合并需要O(n1+n2)

算法中的逻辑迭代:-

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

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

【讨论】:

以上是关于这段代码的时间复杂度是多少(来自 leetcode)?的主要内容,如果未能解决你的问题,请参考以下文章

这段简单的代码有多复杂?

该代码的时间复杂度是多少?

LeetCode 241 的时间和空间复杂度是多少。添加括号的不同方法?

LeetCode 5219. 每个小孩最多能分到多少糖果

找出数组中第k大的数(时间复杂度分析C++代码实现). TopK in array. ( leetcode - 215 )

找出数组中第k大的数(时间复杂度分析C++代码实现). TopK in array. ( leetcode - 215 )