利用堆求最大或最小的前k个数

用堆在海量数据中找出最大或最小的k个数,效率非常高。

1、在一组数据中找出最小的k个数
解题思路:
    要找出最小的k个数,我们可以先用这组数据中的k个数构建一棵“最大堆”,然后再将剩下的元素与堆顶元素相比。如果大于堆顶元素,则不做处理,继续向下比较。如果小于堆顶元素,则将堆顶元素与这个元素交换,然后再恢复堆序,继续向下比较。这样的话最后这个堆里面保存的就是最小的k个数。
例:在{10,16,18,12,11,13,15,17,14,19}里面找出最小的4个数 
1、用前四个数构造一颗最大堆


(2、用剩下的数与堆顶元素进行比较,如图:


2、在一组堆中找出最大的前k个数
与找最小的数是相同的道理,不过找最大的数时要建立一个k个数的最小堆。

时间复杂度分析:
建立一个k个数的堆:O(k*lgk)
向后比较:O((N-k)*lgk)

时间复杂度为:O(N*lgK)


//代码
#pragma once
#include<vector>
#include<cassert>
using namespace std;


template<typename T>
struct SmallNum                  //求最小的数,建最大堆
{
    bool operator()(const T& l,const T& r)
    {
        return l < r;
    }
};

template<typename T>
struct GreatNum                 //求最大的数,建最小堆
{
    bool operator()(const T& l,const T& r)
    {
        return l>r;
    }
};



template<typename T,class Compare=SmallNum<T>>      //默认求最小的k个数
class HeapSearch
{
public:
    HeapSearch()
    {}
    HeapSearch(T* a, int size,int k)
    {
        assert(size>=k);
        assert(k>0);
        //建一个k个数的堆,如果求最小的k个数则建最大堆,反之建最小堆
        _a.reserve(k);
        for (int i = 0; i < k; ++i)
        {
            _a.push_back(a[i]);
        }

        for (int i = (k - 2) / 2; i >= 0; --i)
        {
            //向下调整算法
            AdjustDown(i,k);
        }

        FindKNum(a,size,k);
    }

    void Display()
    {
        for (size_t i = 0; i < _a.size(); i++)
        {
            cout << _a[i] << " ";
        }
        cout << endl;
    }

protected:
    void AdjustDown(int root,int k)
    {
        assert(!_a.empty());
        int parent= root;    //最后一个非叶子结点
        int child = parent * 2 + 1;      //求左孩子结点的下标
        while (child<k)
        {
            if ((child + 1) < k&&Compare()(_a[child],_a[child+1]))
                child++;
            if (Compare()(_a[parent], _a[child]))
            {
                swap(_a[parent],_a[child]);
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break;
            }
        }
    }

    void  FindKNum(T*a ,int size,int k)
    {
        for (int i = k; i < size;i++)
        {
            if (Compare()(a[i], _a[0]))
            {
                _a[0] = a[i];
                AdjustDown(0,k);
            }
        }
    }
private:
    vector<T> _a;
};


你可能感兴趣的:(利用堆求最大或最小的前k个数)