Leetcode: 23. Merge k Sorted Lists (week1 --- hard)

Merge k Sorted Lists:

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

题意:

把k个已经排好序的队列重新排成一个完整的队列

题解:

这一道题的含义就跟归并排序的合并步骤一样,也就是有序队列的合并。

  1. 一开始想直接进行k的队列的每一次寻找最小值然后进行合并,但是看到了这一道题的难度是hard,所以上面的算法的效率是非常低的:设元素的数目为N个,所以大概的比较次数为k*N,也就是每个元素的都需要进行k次比较,时间消耗一定会比较巨大,所以这种方法是行不通的。
  2. 这一道题是在搜索分治算法的时候找到的,但是觉得这一道题大概跟分治的关系也就是前面所提到的?所以可能没有分治思维的方法。
  3. 然后就开始考虑进行排序算法,排序算法最快的速度也就是nlogn,效率还不如寻找最大值。
  4. 在思考排序算法的时候刚好想到了堆排序,想起了之前数据结构老师说的堆排序是很重要的,然后想到了建堆的步骤,以及插入和删除,而这个合并有序序列只需要提供最小值也就可以了,所以用小顶堆是一个不错的算法,因为一开始建堆需要的比较次数为klogk,然后每一次删除以及插入所需要的比较次数是logk,所以总的复杂度是Nlogk,比起k*N算是一个不错的进步了,特别是k很多的时候体现会更好。

 所以解题的代码如下:

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //获取向量的大小
        length = lists.size();
        
        //利用迭代器删除向量中的空元素,从测例中发现的[[]]等样例
        vector<ListNode*>::iterator it = lists.begin();
        for(;it != lists.end();)
        {
            if(*it == nullptr)
            {
                it = lists.erase(it);
                length--;
            }

            else
                //迭代器指向下一个元素位置
                ++it;
        }
        //不存在元素时返回空值
        if(length == 0){
            return nullptr;
        }
        //只有一个非空有序序列时返回该序列的指针
        if(length == 1){
            return lists[0];
        }

        //建堆
        for (int i = length / 2 - 1; i >= 0; i--)
        MinHeapDown(lists, i, length);

        //声明结果头节点
        ListNode * a = new ListNode(lists[0]->val);
        //声明辅助节点
        ListNode * temp = a;
        while(length != 1){
            MinHeapDelete(lists);
            if(delete_->next != nullptr){
                MinHeapAdd(lists, length, delete_->next);
            }
            temp->next = new ListNode(lists[0]->val);
            temp = temp->next;
        }
        //用于只剩一个或者两个有序序列时的元素的补充(因为有可能另一个被删除了,但是还剩余元素)
        if(length == 1){
            lists[0] = lists[0]->next;
            while(delete_->next != NULL && lists[0] != NULL){
                if(delete_->next->val > lists[0]->val){
                    temp->next = new ListNode(lists[0]->val);
                    temp = temp->next;
                    lists[0] = lists[0]->next;
                }
                else{
                    temp->next = new ListNode(delete_->next->val);
                    temp = temp->next;
                    delete_ = delete_->next;
                }
            }
            while(lists[0] != NULL){
                temp->next = new ListNode(lists[0]->val);
                temp = temp->next;
                lists[0] = lists[0]->next;
            }

            while(delete_->next != NULL){
                temp->next = new ListNode(delete_->next->val);
                temp = temp->next;
                delete_ = delete_->next;
            }
        }
        return a;
    };
    //添加新元素
    void MinHeapAdd(vector<ListNode*>& lists, int n, ListNode * new_){
        length++;
        lists[length - 1] = new_;
        MinHeapUp(lists, length - 1);
    }
    //删除元素
    void MinHeapDelete(vector<ListNode*>& lists){
        length--;
        swap(lists[0], lists[length]);
        delete_ = lists[length];
        MinHeapDown(lists, 0, length);
    }

    //添加新的元素(添加到尾部后再进行重新排序)
    void MinHeapUp(vector<ListNode*>& lists, int n){
        int j;
        ListNode* temp = lists[n];
        j = (n - 1) / 2;
        while(j >= 0 && n != 0){
            if (lists[j]->val <= temp->val) break;
            lists[n] = lists[j];
            n = j;
            j = (n - 1) / 2;
        }
        lists[n] = temp;
    }
    //堆排序的下沉算法(用于堆的建立以及元素的删除)
    void MinHeapDown(vector<ListNode*>& lists, int i, int n){
        int j;
        ListNode* temp = lists[i];
        j = 2 * i + 1;
        while(j < n){
            if(j + 1 < n && lists[j + 1]->val < lists[j]->val) j++;
            if(lists[j]->val > temp->val) break;
            lists[i] = lists[j];
            i = j;
            j = 2 * i + 1;
        }
        lists[i] = temp;
    }
    //用于本地的代码测试(输出结果)
    void print(ListNode *result){
        ListNode * temp = result;
        while(temp != NULL && temp->next!=NULL){
            cout << temp->val << " -> ";
            temp = temp->next;
        }
        if(temp == NULL) return ;
        cout << temp->val << endl;
    }

private:
    //用于存储向量的大小
    int length;
    //用于存储最近的被删除的节点(为了记录需要重新加入的元素的上一个结点)
    ListNode * delete_;
};

在写这一部分代码的时候还是有点手生的,然后参考了一下https://blog.csdn.net/hrn1216/article/details/51465270(最小堆 构建、插入、删除的过程图解)这一篇博客,因为有图解看起来会比较好懂些。

后记:

在disscuss中查找了一下min heap C++之后看到了一个

Beats 100% method, using min heap by make_heap, pop_heap, push_heap

的答案,看了一下原来STL还自带提供了min heap的有关的函数方法,而且还优化的这么好,所以就学习了一波。

default (1)
template <class RandomAccessIterator>
  void make_heap (RandomAccessIterator first, RandomAccessIterator last);
custom (2)
template <class RandomAccessIterator, class Compare>
  void make_heap (RandomAccessIterator first, RandomAccessIterator last,
                  Compare comp );
Rearranges the elements in the range  [first,last) in such a way that they form a  heap.
A heap is a way to organize the elements of a range that allows for fast retrieval of the element with the highest value at any moment (with  pop_heap), even repeatedly, while allowing for fast insertion of new elements (with  push_heap).
The element with the highest value is always pointed by first. The order of the other elements depends on the particular implementation, but it is consistent throughout all heap-related functions of this header.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值