zoukankan      html  css  js  c++  java
  • [LeetCode] Merge k Sorted Lists

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

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

    Solution:

    1. O(nk2) runtime, O(1) space – Brute force:

    The brute force approach is to merge a list one by one. For example, if the lists = [l1, l2, l3, l4], we first merge l1 and l2, then merge the result with l3, and finally l4.

    To analyze its time complexity, we are going to assume there are a total of k lists, and each list is of size n. There will be a total of k–1 merge operations. The first merge operation will be two lists of size n, therefore in the worst case there could be n + n comparisons. The second merge operation will be two lists of size 2n and n. Notice that each merge increase the size of the merged lists by n. Therefore, the total number of comparisons required is 2n + 3n + 4n + … + kn = n(k(k+1)/2 -1) = O(nk2).

    2. O(nklogk) runtime, O(k) space – Heap:
    We could use a min heap of size k. The heap is first initialized with the smallest element from each list. Then as we extract the nodes out from the heap, we must remember to insert its next node into the heap. As each insert operation into the heap costs log(k) and there are a total of nk elements, the total runtime complexity is O(nklogk).

    Ignoring the extra space that is used to store the output list, we only use extra space of O(k) due to the heap.

    /**
     * Author : Acjx
     * Email  : zhoujx0219@163.com
     */
    
    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
    
    class Cmp
    {
        public:
            bool operator() (const ListNode *lhs, const ListNode *rhs)
            {
                return lhs->val > rhs->val;
            }
    };
    
    class Solution 
    {
    public:
        ListNode *mergeKLists(vector<ListNode *> &lists) 
        {
            if (lists.empty()) return NULL;
            
            priority_queue<ListNode, vector<ListNode *>, Cmp> queue;
            
            for (ListNode *&nodePtr : lists)
            {
                if (nodePtr != NULL)
                {
                    queue.push(nodePtr);
                }
            }
            
            ListNode *dummyHead = new ListNode(0);
            ListNode *p = dummyHead;
            while (!queue.empty())
            {
                ListNode *nodePtr = queue.top();
                queue.pop();
                p->next = nodePtr;
                p = p->next;
                if (nodePtr->next != NULL)
                {
                    queue.push(nodePtr->next);
                }
            }
            
            return dummyHead->next;
        }
    };

    3. O(nk log k) runtime, O(1) space – Divide and conquer using two way merge:
    If you still remember how merge sort works, we can use a divide and conquer mechanism to solve this problem. Here, we apply the merge two lists algorithm from Article[Merge Two Sorted Lists].

    Basically, the algorithm merges two lists at a time, so the number of lists reduces from:

    k –> k/2 –> k/4 –> … –> 2 –> 1

    Similarly, the size of the lists increases from (Note that the lists could subdivide itself at most log(k) times):

    n –> 2n –> 4n –> … –> 2logkn      

    Therefore, the runtime complexity is:

    k * n + k/2 * 2n + k/4 * 4n + … + 2logkn * 1
    = nk + nk + nk + … + nk
    = nklogk

    Since we are implementing this divide and conquer algorithm iteratively, the space complexity is constant at O(1), yay!

    /**
     * Author : Acjx
     * Email  : zhoujx0219@163.com
     */
    
    /**
     * 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) 
        {
            if (lists.empty()) return NULL;
            
            int end = lists.size() - 1;
            while (end > 0)
            {
                int begin = 0;
                while (begin < end)
                {
                    lists[begin] = merge2Lists(lists[begin], lists[end]);
                    ++begin;
                    --end;
                }
            }
            return lists.at(0);
        }
        
    private:
        ListNode *merge2Lists(ListNode *l1, ListNode *l2)
        {
            ListNode *dummyHead = new ListNode(0);
            ListNode *p = dummyHead;
            
            while (l1 != NULL && l2 != NULL)
            {
                if (l1->val < l2->val)
                {
                    p->next = l1;
                    l1 = l1->next;
                }
                else
                {
                    p->next = l2;
                    l2 = l2->next;
                }
                p = p->next;
            }
            
            if (l1 != NULL) p->next = l1;
            if (l2 != NULL) p->next = l2;
            
            return dummyHead->next;
        }
    };
  • 相关阅读:
    qemu+chroot构建arm aarch64虚拟机
    <转>Linux环境下段错误的产生原因及调试方法小结
    <转>PCA的数学原理
    博客分类整理
    detectron2 配置记录
    如何读取部分的预训练模型
    重新配置语义分割实验环境遇到的坑
    pytorch 调整tensor的维度位置
    seg代码配置的踩坑记录
    Alienware R8外星人台式机安装双系统(WIN10+Ubuntu)的总结
  • 原文地址:https://www.cnblogs.com/jianxinzhou/p/4314404.html
Copyright © 2011-2022 走看看