Merge k Sorted Lists

Merge k Sorted Lists

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

Example:

Input:
[
  1->4->5,
  1->3->4,
  2->6
]
Output: 1->1->2->3->4->4->5->6

简单粗暴的写法,依次合并2两个list

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergrlist(ListNode* list1, ListNode* list2)
    {
        ListNode newNode(-1);
        ListNode* pNode = &newNode;
        while (list1 && list2)
        {
            if (list1->val < list2->val)
            {
                pNode->next = list1;
                list1 = list1->next;
            }
            else
            {
                pNode->next = list2;
                list2 = list2->next;
            }
            pNode = pNode->next;
        }
        pNode->next = list1 ? list1 : list2;
        return newNode.next;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.empty())
            return NULL;
        if (lists.size() == 1)
            return lists[0];
        ListNode* result = lists[0];
        for (int i = 1; i < lists.size(); i++)
        {
            result = mergrlist(result, lists[i]);
        }
        return result;
    }
};

优化写法:
将每个链表的表头元素取出来,建立一个小顶堆,因为k个链表中都排好序了,因此每次取堆顶的元素就是k个链表中的最小值,可以将其合并到合并链表中,再将这个元素的指针指向的下一个元素也加入到堆中,再调整堆,取出堆顶,合并链表。。。。以此类推,直到堆为空时,链表合并完毕。 链接:这里写链接内容

class Solution {
public:
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        // 使用堆排序, 
        // 1. 选出每个链表的头来插入小顶堆中,
        // 2. 再把堆顶接入合并链表中,
        // 3. 被选出的指针后移再加入小顶堆中,回到2
        // 4. 最后所有链表都为空时,返回合并链表的头指针
        if(lists.empty()) return nullptr;
        vector<ListNode* > heap;
        // 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.empty()){
            auto minNode = popHeap(heap);
            p->next = minNode; // 接入链表
            p = p->next;
            // 3. 被选出的指针后移再加入小顶堆中,回到2
            auto next = minNode->next;
            if(next) pushHeap(heap, next);
        }
        // 4. 最后所有链表都为空时,返回合并链表的头指针
        return head.next;
    }
    // 建立小顶堆
    // 自低向上
    void makeHeap(vector<ListNode*> &heap){
        // 从最后一个元素的父节点开始建立小顶堆
        for(int i = heap.size()/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()+1) && heap[l-1]->val<heap[i-1]->val ){
            // 如果没有超过边界并且左孩子比父亲小,则换
            least = l;
        }
        if(r<heap.size()+1 && heap[r-1]->val<heap[least-1]->val){
            // 如果没有超过边界并且右孩子最小,则换
            least = r;
        }
        if(least != i){
            swap(heap[i-1], heap[least-1]);
            minHeap(heap, least);
        }
    }
    // 在小顶堆中插入一个元素
    // 自低向上
    void pushHeap(vector<ListNode*> &heap, ListNode* p){
        heap.push_back(p);
        int child = heap.size();
        int parent = child/2;
        for(int child = heap.size(),parent = child/2; parent; child--, parent = child/2){
            if(heap[child-1]->val < heap[parent-1]->val){
                swap(heap[child-1], heap[parent-1]);
            }
        }
    }
    // 弹出堆顶
    ListNode* popHeap(vector<ListNode*> &heap){
        swap(heap[0], heap[heap.size()-1]);
        auto p = heap.back();
        heap.pop_back();
        minHeap(heap, 1);
        return p;
    }
};

继续优化:
后来想到既然堆每次加入一个元素的时候都要调整堆顶,那么每次把要添加的元素换到堆顶再调整就不用写pushHeap的函数了,当要添加的元素为空时,相当于执行popHeap函数,因此可以简化代码:

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

整体而言,复杂度从O(nk²)降到O(nk²)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值