zoukankan      html  css  js  c++  java
  • 23.Merge k Sorted Lists (Array, Queue; Sort)

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

    思路I: 选择排序

    每次都比较各个list的头指针所指的val,取最小的那个。时间复杂度O(n*k)

    class Solution {
    public:
        ListNode *mergeKLists(vector &lists) {
            if(lists.empty()) return NULL;
            ListNode *head = new ListNode(INT_MIN); //创建头指针,不指向任何List元素;最后返回的是head->next
            head->next = lists[0];
            ListNode *p1;
            ListNode *p2;
            ListNode *tmp;
            for(int i = 1; i< lists.size(); i++)
            {
                p1 = head;
                p2 = lists[i];
                while(p1->next && p2)
                {
                    if(p1->next->val <= p2->val) p1 = p1->next;
                    else
                    {
                        tmp = p1->next;
                        p1->next = p2;
                        p1 = p1->next;
                        p2 = p2->next;
                        p1->next = tmp;
                    }
                }
                if(p2)
                {
                    p1->next = p2;
                }
            }
            return head->next;
        }
    };

    Result:Time Limit Exceeded

    思路II: 最小堆。

    时间复杂度:堆排序其实也是一种选择排序。只不过直接选择排序中,为了从R[1...n]中选择最大记录,需比较n-1次,然后从R[1...n-2]中选择最大记录需比较n-2次。事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。时间复杂度为O(nlogk)

    堆排序思想参考:http://jingyan.baidu.com/article/5225f26b057d5de6fa0908f3.html 

    class Solution {
    public:
        ListNode *mergeKLists(vector<ListNode *> &lists) {
            // 使用堆排序, 
            // 1. 选出每个链表的头来插入小顶堆中,
            // 2. 再把堆顶接入合并链表中,
            // 3. 被选出的指针后移再加入小顶堆中,回到2
            // 4. 最后所有链表都为空时,返回合并链表的头指针
            if(lists.empty()) return nullptr;
            vector<ListNode* > heap;
            heap.push_back(0); //padding
            // 1. 选出每个链表的头来插入小顶堆中,
            for(int i = 0; i != lists.size(); i ++){
               if(lists[i]) heap.push_back(lists[i]);
            }
            makeHeap(heap);
            // 2. 再把堆顶接入合并链表中,
            ListNode head(-1); // 合并链表的表头
            ListNode* p = &head;
            while(heap.size()>1){
                auto minNode = heap[1];
                p->next = minNode; // 接入链表
                p = p->next;
                // 3. 被选出的指针后移再加入小顶堆中,回到2
                auto next = minNode->next;
                if(next) {
                    heap[1] = next;
                }else{
                    swap(heap[1], heap[heap.size()-1]);
                    heap.pop_back();
                }
                minHeap(heap, 1);//加入新元素到堆顶后,自上向下调整
            }
            // 4. 最后所有链表都为空时,返回合并链表的头指针
            return head.next;
        }
        // 建立小顶堆
        // 自底向上
        void makeHeap(vector<ListNode*> &heap){
            // 从最后一个元素的父节点开始建立小顶堆
            for(int i = (heap.size()-1)/2; i >0 ; i --){
                minHeap(heap, i);
            }
        }
        // 小顶堆,以第i个元素为根建立小顶堆
        //位置从1开始,取元素时记得-1
        // 自顶向下
        void minHeap(vector<ListNode*> &heap, int i){
            int l = i*2;
            int r = l+1;
            int least(i);
            // 算出最小元素的位置
            if((l< heap.size()) && heap[l]->val<heap[i]->val ){
                // 如果没有超过边界并且左孩子比父亲小,则换
                least = l;
            }
            if(r<heap.size() && heap[r]->val<heap[least]->val){
                // 如果没有超过边界并且右孩子最小,则换
                least = r;
            }
            if(least != i){
                swap(heap[i], heap[least]);
                minHeap(heap, least);//换了之后,继续向下调整
            }
        }
    };

    C++的优先队列底部就是用堆排序实现的,所以这里可以使用优先队列。

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     ListNode *next;
     *     ListNode(int x) : val(x), next(NULL) {}
     * };
     */
     
    struct cmp {
        bool operator () (ListNode *a, ListNode *b) {
            return a->val > b->val;
        }
    };
    
    class Solution {
    public:
        ListNode* mergeKLists(vector<ListNode*>& lists) {
            priority_queue<ListNode*,vector<ListNode*>,cmp> queue;
            for (int i = 0; i < lists.size(); i++) {
                if (lists[i] != NULL) {
                    queue.push(lists[i]);
                }
            }
            
            ListNode *head = new ListNode(INT_MIN);
            ListNode *prev = head, *temp;
            while (!queue.empty()) {
                temp = queue.top();
                queue.pop();
                prev->next = temp;
                prev = prev->next;
    
                if (temp->next != NULL) {
                    queue.push(temp->next);
                }
            }
            return head->next;
        }
    };

    优先队列默认从大到小排序,即大顶堆,所以这里我们需要改成小顶堆:

    1. 基本类型(例如int)都可以用greater<type>声明小顶堆优先队列:

    priority_queue<int, vector<int>, greater<int>> q

    2. 自定义的数据结构必须自定义比较函数(如以上代码中定义的cmp),或者自定义 > 操作:

    bool operator > (ListNode* a, ListNode* b){
        return a->val > b->val;
    }

    思路III:归并排序

    初始状态:6,202,100,301,38,8,1
    第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
    第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
    第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
    reference: http://baike.baidu.com/link?url=ayX3MQx_CrmcjOxkL7EKhXukLH9pJKJsD1XDMaP6eQwvFfc-BtnQBUTsElRafXbxqhCFOIlKC5VsL14LgjEjIK 
    归并排序时间复杂度O(nlogn)
    /**
     * 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;
            
            mergeSort(lists, 0, lists.size()-1);
            return lists[0];
        }
        void mergeSort(vector<ListNode*>& lists, int start, int end){
            if(start==end) return;
            int mid = (start + end) >> 1;
            mergeSort(lists, start, mid);
            mergeSort(lists, mid+1, end); 
            merge(lists,start, mid+1);
        }
        
        void merge(vector<ListNode*>& lists, int lst1, int lst2){
            ListNode* root = NULL;
            ListNode* current = NULL;
            
            while(lists[lst1] && lists[lst2]){
                if(lists[lst1]->val <= lists[lst2]->val){
                    if(!root){
                        root = lists[lst1];
                        current = root;
                    }
                    else{
                        current->next = lists[lst1];
                        current = current->next;
                    }
                    lists[lst1] = lists[lst1]->next;
                }
                else{
                    if(!root){
                        root = lists[lst2];
                        current = root;
                    }
                    else{
                        current->next = lists[lst2];
                        current = current->next;
                    }
                    lists[lst2] = lists[lst2]->next;
                }
            }
            
            while(lists[lst1]){
                if(!root){
                    root = lists[lst1];
                    current = root;
                }
                else{
                    current->next = lists[lst1];
                    current = current->next;
                }
                lists[lst1] = lists[lst1]->next;
            }
            while(lists[lst2]){
                if(!root){
                    root = lists[lst2];
                    current = root;
                }
                else{
                    current->next = lists[lst2];
                    current = current->next;
                }
                lists[lst2] = lists[lst2]->next;
            }
            
            lists[lst1] = root;
        }
    };
  • 相关阅读:
    C++雾中风景16:std::make_index_sequence, 来试一试新的黑魔法吧
    神经网络中的降维和升维方法 (tensorflow & pytorch)
    论文翻译:2020_Residual Acoustic Echo Suppression Based On Efficient Multi-Task Convolutional Neural Network
    论文翻译:2018_Deep Learning for Acoustic Echo Cancellation in Noisy and Double-Talk Scenarios
    十分钟入门Apollo
    SpringSecurity实现OAuth2+JWT
    实现Vue的多页签组件
    原生redis命令
    .NET Core学习笔记(8)——Entity Framework Core之Database First
    函数极限的计算_计算机程序化实现的理论基础
  • 原文地址:https://www.cnblogs.com/qionglouyuyu/p/4721853.html
Copyright © 2011-2022 走看看