c++ - 使用 STL 仅保留 N 个最小元素(有重复项)

标签 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

问题 1:必须更改哪些内容才能允许重复?
问题 2:如何使这段代码更快,同时又不浪费 GB 的内存来存储所有输入元素? (现在的代码实在是太慢了)。

最佳答案

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

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

这可以使用 C++ 库函数轻松完成 std::make_heap , std::pop_heap , 和 std::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

当然你可以把它变成一个函数并替换硬编码的5N .

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

仅当检测到新项满足成为最小的 N 个元素之一时才对最大堆进行操作,这很容易通过检查堆中的顶部项并将其与正在处理的新项进行比较来完成已处理。


如果你想获得 5 个最大的项目,你将构建一个最小堆而不是最大堆。要做到这一点,唯一的改变是调用一个仿函数来改变堆函数中的比较运算符,并将测试改为>=。而不是 <= :

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

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 min-heap of the 5 elements   
                std::make_heap(s.begin(), s.end(), std::greater<int>());
            continue;
        }

        // now check if the next element is larger 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(), std::greater<int>());
                   
            // 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(), std::greater<int>());
        }
    }

    // sort the heap    
    std::sort_heap(s.begin(), s.end(), std::greater<int>());

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

    std::cout << '\n';

    return 0;
}

输出:

9 8 8 7 6 

关于c++ - 使用 STL 仅保留 N 个最小元素(有重复项),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56507928/

相关文章:

c++ - 在调用 vector::assign() 之前调用 vector::reserve() 会更好吗?

c++ - std::sort 与 intel ipp 排序性能对比。我究竟做错了什么?

c++ - 指示文件结束

c++ - 寻找 std::boyer_moore_searcher

c++ - 自定义 STL 序列的最小嵌套 typedef 集?

C++ 避免 vector <bool>实例化

c++ - 使用 opencv C++ 进行面部定向

c++ - 使用 cin.getline() 后清除 cin 缓冲区时出现问题

c++ - 检测到内存泄漏

c++ - 为什么 std::transform 不将 std::string vector 转换为 unsigned int vector ?