欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

剑指offer·之30:最小的K个数

程序员文章站 2022-07-10 14:08:05
...

题目:

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

分析:

利用堆排序,O(N logK),适合处理海量数据

(1) 遍历输入数组,将前k个数插入到推中;(利用multiset来做为堆的实现)

用前k个数字来建立大顶堆,而后拿后面的后面的n-k个元素依次与大顶堆中的最大值(即堆顶)元素比较,

(2) 继续从输入数组中读入元素做为待插入整数,并将它与堆中最大值比较:如果待插入的值比当前已有的最大值小,则用这个数替换当前已有的最大值;如果待插入的值比当前已有的最大值还大,则抛弃这个数,继续读下一个数。

如果小于该最大元素,则用该元素替换掉堆顶元素,并调整堆使其维持大顶堆的结构,如果大于该最大元素,则直接跳过,继续拿下一个数字与堆顶元素比较,等到所有的元素比较并操作完,这时数组中后面的元素都比该大顶堆中的数字要大,那么该大顶堆中的k个数字变为数组中最小的k个数字,且堆顶元素为这k个最小数组中最大的,因此它又是数组中第k小的数字。

这样动态维护堆中这k个数,以保证它只储存输入数组中的前k个最小的数,最后输出堆即可。

该算法建立大顶堆需要的时间为O(k),每次调整堆需要的时间为O(logk),而总共要调整n-k次,因此时间复杂度为

O(k+(n-k)logk),当k远远小于n时,时间复杂度可近似为O(nlogk)。

代码:

vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
    {
        vector<int> result;
        int len = input.size();
        if(input.empty() || k<=0 || len < k) return result;
         
        multiset<int, greater<int> > leastNumbers; // 从大到小排序
        multiset<int, greater<int> >::iterator iterGreater; // 定义迭代器
         
        vector<int>::iterator iter = input.begin();
        for(; iter != input.end(); ++iter)
        {
            // 将前k个数直接插入进multiset中,注意是小于K
            if(leastNumbers.size() < k)
            {
                leastNumbers.insert(*iter);
            }
            else
            {
                // 因为设置的从大到小排序,故multiset中第一个位置的元素即为最大值
                iterGreater = leastNumbers.begin();
                 
                // 如果input中当前元素比multiset中最大元素小,则替换;即保持multiset中这k个元素是最小的。
                if(*iter < *(leastNumbers.begin()))
                {
                    // 替换掉当前最大值
                    leastNumbers.erase(iterGreater);
                    leastNumbers.insert(*iter);
                }
            }
        }
         
        for(iterGreater = leastNumbers.begin();iterGreater!=leastNumbers.end();++iterGreater)
        {
            result.push_back(*iterGreater); // 将multiset中这k个元素输出
        }
         
        return result;
    }