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;
    }
};
原文地址:https://www.cnblogs.com/qionglouyuyu/p/4721853.html