剑指offer-数组中最小的K个数

题目描述:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

这是一个经典的topK问题。
根据一般的想法,对数组进行排序,再去前K个数作为结果。排序算法若采用快排,平均时间复杂度为O(n*logn)。
因此常用解法为构建最大(小)堆的策略来解topK问题,这种解法的平均时间复杂度为O(n*logk),所以很适合于海量数据中的topK的问题。思路如下:
1、建立容量为k的最大堆,最大堆的定义为树的根节点的值为最大值。
2、遍历输入数组,若最大堆的容量未满,则继续将数组中的元素放入最大堆中。
3、若最大堆已满,则根据数组中下一个元素与堆中最大元素比较,若大于最大元素则丢弃;若小于最大元素,则将堆中的最大元素删掉erase,并将数组的该元素放入堆中insert,得到重新排序后的最大堆。
考虑到面试过程中时间限制,无法重现最大堆的算法,因此常用STL中的multiset容器来实现最大堆,保证能快速实现查询、删除、插入的操作。multiset底层是采用红黑树来实现的,可以通过O(logk)的时间实现上述三步,对于数组中的n个数则时间复杂度为O(n*logk)。
在处理海量数据(上千万or亿条数据)时,相比于普通的排序后取数,时间复杂度为O(n*logn)的方法,最大堆算法的时间复杂度O(n*logk)将减少几十个数量级。
代码如下:

class Solution {
public:
    typedef multiset<int, greater<int>> intset;
    typedef multiset<int, greater<int>>::iterator setiterator;
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
    {
        intset leastnum;
        return GetLeastNumbers_Solution(input,k,leastnum);
    }
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k, intset& leastNumbers) {
        vector<int> rs;
        leastNumbers.clear();
        int size = input.size();
        if (k < 0 || size < k)
            return rs;
        vector<int>::iterator iter = input.begin();
        for (; iter < input.end(); ++iter)
        {
            if (int(leastNumbers.size()) < k)
            {
                leastNumbers.insert(*iter);
            }
            else
            {
                if (*iter < *leastNumbers.begin())
                {
                    leastNumbers.erase(leastNumbers.begin());
                    leastNumbers.insert(*iter);
                }
            }
        }
        while (!leastNumbers.empty())
        {
            rs.push_back(*leastNumbers.begin());
            leastNumbers.erase(leastNumbers.begin());
        }
        rs.reserve(rs.size());
        return rs;
    }
};

你可能感兴趣的:(剑指offer)