海量数据处理 大量数据中找出最大的前10个数 (Top K 问题)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了海量数据处理 大量数据中找出最大的前10个数 (Top K 问题)相关的知识,希望对你有一定的参考价值。

参考技术A 在工作中我们常遇到此类问题,从一个大量甚至海量的数据中取出前几个大的数。必须在海量的文章中取出点击量最大的10篇文章。
此类问题其实就是Top K问题。
给定一个数据(数据量海量 N),想找到前 K 个最大的或最小的元素。

eg:有10亿个Long型整数,存储在一个文件中,如果找出其中最大的10个?
最容易想到的方法是将数据全部排序,然后在排序后的集合中进行查找,最快的排序算法的时间复杂度一般为O(nlogn),如快速排序。每个Long类型占8个字节,10亿个数就要占用7GB+的存储空间,对于一些可用内存小于7GB的计算机而言,很显然是不能一次将全部数据读入内存进行排序的。其实即使内存能够满足要求(我机器内存都是8GB),该方法也并不高效,因为题目的目的是寻找出最大的10个数即可,而排序却是将所有的元素都排序了,做了很多的无用功。

第二种方法采用最小堆。首先读入前10个数来创建大小为10的最小堆,然后遍历后续的数字,并于堆顶(最小)数字进行比较。如果比最小的数小,则继续读取后续数字;如果比堆顶数字大,则替换堆顶元素并重新调整堆为最小堆。整个过程直至10亿个数全部遍历完为止。然后按照中序遍历的方式输出当前堆中的所有10个数字。这个方法使用的内存是可控的,只有10个数字所需的内存即可。

堆的应用之海量数据处理(红包大乱战)

应用场景:2015年春节期间,A公司的支付软件某宝和T公司某信红包大乱战。春节高峰以后,公司Leader要求后台攻城狮对后台的海量的数据进行分析。先要求分析出各地区发红包最多的前100位用户。现在知道人最多的S地区大约有100W用户,要求写一个算法实现。

分析:看到这里,问题可以简化为求很多个数据中的前100个节点,然而这很多个数据磁盘根本放不下,并且找出前100个数比较复杂,因此我们可以借助堆来实现。这样数据从磁盘里读,而内存中只有100个数据。那么到底是建大堆还是建小堆来解决呢?我们知道大堆就是父节点都大于孩子节点,小堆就是父节点都小于孩子节点,当我们找到的数大于最后一个节点时堆就应该排序一次,因此我们应该建一个小堆,根节点就是100里面最小的,用父节点与需要比较的数据进行比较,如果比根节点大,那么需要入堆进行排序,否则就不需要排序。实现代码如下:

void CreateRedPacket(vector<int>& moneys)  //创建红包数据
{
    srand(time(0));
    moneys.reserve(N);
    for (int i = 0; i < N; ++i)
    {
        moneys.push_back(rand() % 10000);
    }
    for (int j = N - K; j < N; ++j)
    {
        moneys[j] = rand() % N;
    }
}

void AdjustDown(int*a, size_t size, int root)    //向下调整
{
    int parent = root;
    int child = parent * 2 + 1;
    while (child < size)
    {
        while (child + 1 < size&&a[child + 1] > a[child])   //找出左右节点中值最大的那个
            child++;

        if(a[parent] < a[child])
        {
            swap(a[parent], a[child]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

void GetTopK(vector<int>& moneys)   //获取前K个数据
{
    int arrays[K] = { 0 };
    for (size_t i = 0; i < K; ++i)
    {
        arrays[i] = moneys[i];
    }

    for (int i = (K- 2) / 2; i>=0; ++i)   //先建一个数据个数为K的堆
    {
        AdjustDown(arrays, K, 0);
    }

    for (size_t i = K; i < N; ++i)    //把剩下的数据一一跟对顶元素比较,如果数据小于堆顶元素就赋值给堆顶元素并向下调整
    {
        if (arrays[0] < moneys[i])
        {
            arrays[0] = moneys[i];
            AdjustDown(arrays,  K, i);
        }
    }
    for (int i = 0; i < K; ++i)   //打印出前K个数据
    {
        cout << arrays[i] << "  ";
    }
    cout << endl;
}

void TestTopK()
{
    vector<int> moneys;
    CreateRedPacket(moneys);
    GetTopK(moneys);
}

以上是关于海量数据处理 大量数据中找出最大的前10个数 (Top K 问题)的主要内容,如果未能解决你的问题,请参考以下文章

Top k 问题

海量数据处理 - 10亿个数中找出最大的10000个数(top K问题)

海量数据处理 - 10亿个数中找出最大的10000个数(top K问题)

top K问题

找工作——大数据的处理方式

海量数据中找出出现次数最多的前10个URL