使用STL仅保留N个最小的元素(重复)

时间:2019-06-08 15:55:32

标签: c++ stl

下面的MVCE尝试从大的随机元素输入输入流(包含重复项)中按升序仅输出最小的5个元素。

int main(int argc, char *argv[])
{
    std::set<int> s;   //EDIT:  std::multiset is an answer to Q1

    for (int i : {6, 6, 5, 8, 3, 4, 0, 2, 8, 9, 7, 2})  //Billions of elements in reality
    {
        if ( (s.size() < 5) || (i <= *(--s.end())) )  //Insert only if not full or when the element to be inserted is smaller than the greatest one already in the set
        {
            if (s.size() >= 5)  //Limit the number of smallest elements that are kept. In reality ~8000
                s.erase(*(--s.end())); //Erase the largest element

            s.insert(i);
        }
    }

    for (int d: s)
        std::cout << d << " ";  //print the 5 smallest elements in ascending order      

    std::cout << '\n';

    return 0;
}

输出为:

0 2 3 4

输出应为:

0 2 2 3 4

第一季度:必须进行哪些更改才能允许重复?
第二季度:如何在不浪费GB内存来存储所有输入元素的情况下使此代码更快? (现在的代码实在太慢了。)

3 个答案:

答案 0 :(得分:2)

这听起来像是经典的采访问题:“如何在不知道将要处理的数据大小的情况下存储最小的N个项目?”

一个答案是使用N个项目的最大堆,然后调整堆(删除顶部元素,添加新元素,heapify),如果后续项目小于或等于堆。

使用C ++库函数std::make_heapstd::pop_heapstd::push_heap可以轻松完成此操作。

这里是一个例子:

#include <vector>
#include <algorithm>
#include <iostream>

int main(int argc, char *argv[])
{
    std::vector<int> s; 
    for (int i : {6, 6, 5, 8, 3, 4, 0, 2, 8, 9, 7, 2})
    {
        // add the first 5 elements to the vector
        if (s.size() < 5)
        {
            s.push_back(i);
            if ( s.size() == 5 )
                // make the max-heap of the 5 elements   
                std::make_heap(s.begin(), s.end());
            continue;
        }

        // now check if the next element is smaller than the top of the heap
        if (s.front() >= i)
        {
            // remove the front of the heap by placing it at the end of the vector
            std::pop_heap(s.begin(), s.end());

            // get rid of that item now 
            s.pop_back();

            // add the new item 
            s.push_back(i);

            // heapify
            std::push_heap(s.begin(), s.end());
        }
    }

    // sort the heap    
    std::sort_heap(s.begin(), s.end());

    for (int d : s)
        std::cout << d << " ";  //print the 5 smallest elements in ascending order      

    std::cout << '\n';

    return 0;
}

输出:

0 2 2 3 4

当然,您可以将此功能用作功能,并将硬编码的5替换为N

如果有数十亿个元素,即比N多得多的元素,那么堆中唯一保留的就是N个元素。

仅当检测到新项目满足最小N个元素之一时,才操作max-heap,这很容易通过检查堆中的最高项目并将其与正在使用的新项目进行比较来完成处理。

答案 1 :(得分:0)

尝试此操作,无需对所有内容进行排序。只需排序,直到前5个元素在向量的前面,不需要任何额外的内存(就地排序),向量对于插入而言很快。

#include <iostream>
#include <vector>
#include <algorithm>

int main() 
{

    std::vector<int> vec{ 6, 6, 5, 8, 3, 4, 0, 2, 8, 9, 7, 2 };
    int numElements = 5;

    std::partial_sort(vec.begin(), vec.begin() + numElements, vec.end());

    for (int i = 0; i < numElements; ++i)
    {
        std::cout << vec[i]  << "\n";
    }

    return 0;
}

如果您不想存储所有输入,则取决于您如何读取输入,但是解决方案会有所不同。例如,读取块,占用每个块中最小的5个,最后只对每个块的组合“最小5个”再次执行一次。

答案 2 :(得分:0)

Q2回答:转到多重集合的前N个元素(我不确定是将其排序为从最高到最低还是从最低到最高,因此进行调整),然后push_back()将它们设为std::vector