在文件输入期间跟踪最高的5个数字

时间:2015-11-13 07:41:50

标签: c++ algorithm sorting struct

所以我想说我有一个结构

struct largestOwners
{
    string name;
    double amountOwned;
};

我正在使用带有300个名字和数量的ifstream从文件中读取它。

如何在输入过程中跟踪最高的5个数字?所以我不必排序,而是在ifstream输入期间跟踪它。

我的目标是在输入过程中跟踪5个最高金额,以便以后轻松打印出来。并节省时间/处理而不是将来做

我知道我可以将它存储在一个数组或另一个结构中,但是在ifstream输入结构时有什么好的算法来跟踪它吗?

让我们说,当我正在阅读时,文本文件看起来像这样。

4025025 Tony
66636 John
25 Tom
23693296 Brady
363 Bradley
6200 Tim

谢谢!

7 个答案:

答案 0 :(得分:4)

要跟踪传入数字流中最高的5个数字,可以使用大小为5的min-heap(C ++ STL set可以用作最小堆)。

首先使用前5个数字填充min-heap。之后,对于每个传入元素,将其与最大的5个数字(最小堆的根)中的最小值进行比较。如果当前数字小于该值,则不执行任何操作,否则删除第5个最大值(从最小堆弹出)并将当前数字插入最小堆。

删除和插入最小堆将花费O(log n)时间。

例如,请考虑以下数字流:

1 2 5 6 3 4 0 10 3

最小堆最初会有1 2 3 5 6

遇到4时,1会被删除,4会被插入。 最小堆现在看起来像这样:2 3 4 5 6

遇到0时,没有任何反应。

遇到10时,会删除2并插入10。 最小堆现在看起来像这样:3 4 5 6 10

遇到3时,没有任何反应。

所以最后一组5个最大的元素都包含在堆(3 4 5 6 10

你甚至可以调整它以跟踪输入数字流中的k个最高元素。只需将最小堆的大小更改为k。

答案 1 :(得分:2)

在阅读文件时,请保留已查看的5个最大数字(及其所有者)的排序列表。 每当您读取的值高于5的最低值时,请删除最低值并在排序列表中插入新号码。

列表列表可以存储在数组中,也可以存储在任何其他具有订单的数据结构中,您可以在其中实现排序和插入。 (或者已经实施的地方)

除了对列表进行排序之外,您还可以在每次阅读新条目时简单地浏览5个条目(不应该太糟糕,因为5个条目的数量非常小)

答案 2 :(得分:1)

您可以使用标准库函数std::nth_element()

为结构实现比较函数(或重载比较运算符)应该相当容易。然后你只需将文件解析为那些文件的向量并完成它。该算法使用部分排序,平均线性时间。

以下是我在下面链接的文档网站上给出的示例:

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

int main()
{
    std::vector<int> v{5, 6, 4, 3, 2, 6, 7, 9, 3};

    std::nth_element(v.begin(), v.begin() + v.size()/2, v.end());
    std::cout << "The median is " << v[v.size()/2] << '\n';

    std::nth_element(v.begin(), v.begin()+1, v.end(), std::greater<int>());
    std::cout << "The second largest element is " << v[1] << '\n';
}

供参考:

出于好奇,我实施了一些方法:

#include <algorithm>
#include <functional>
#include <queue>
#include <set>
#include <vector>

std::vector<int> filter_nth_element(std::vector<int> v, int n) {
    auto target = v.begin()+n;
    std::nth_element(v.begin(), target, v.end(), std::greater<int>());
    std::vector<int> result(v.begin(), target);
    return result;
}

std::vector<int> filter_pqueue(std::vector<int> v, int n) {
    std::vector<int> result;
    std::priority_queue<int, std::vector<int>, std::greater<int>> q;
    for (auto i: v) {
        q.push(i);
        if (q.size() > n) {
            q.pop();
        }
    }
    while (!q.empty()) {
        result.push_back(q.top());
        q.pop();
    }
    return result;
}

std::vector<int> filter_set(std::vector<int> v, int n) {
    std::set<int> s;
    for (auto i: v) {
        s.insert(i);
        if (s.size() > n) {
            s.erase(s.begin());
        }
    }
    return std::vector<int>(s.begin(), s.end());
}

std::vector<int> filter_deque(std::vector<int> v, int n) {
    std::deque<int> q;
    for (auto i: v) {
        q.push_back(i);
        if (q.size() > n) {
            q.erase(std::min_element(q.begin(), q.end()));
        }
    }
    return std::vector<int>(q.begin(), q.end());
}

std::vector<int> filter_vector(std::vector<int> v, int n) {
    std::vector<int> q;
    for (auto i: v) {
        q.push_back(i);
        if (q.size() > n) {
            q.erase(std::min_element(q.begin(), q.end()));
        }
    }
    return q;
}

我做了一些测试:

#include <random>
#include <iostream>
#include <chrono>

std::vector<int> filter_nth_element(std::vector<int> v, int n);
std::vector<int> filter_pqueue(std::vector<int> v, int n);
std::vector<int> filter_set(std::vector<int> v, int n);
std::vector<int> filter_deque(std::vector<int> v, int n);
std::vector<int> filter_vector(std::vector<int> v, int n);

struct stopclock {
    typedef std::chrono::high_resolution_clock high_resolution_clock;
    std::chrono::time_point<high_resolution_clock> start, end;
    stopclock() : start(high_resolution_clock::now()) {}
    ~stopclock() {
        using namespace std::chrono;
        auto elapsed = high_resolution_clock::now() - start;
        auto elapsed_ms = duration_cast<milliseconds>(elapsed);
        std::cout << elapsed_ms.count() << " ";
    }
};

int main() {
    // randomly initialize input array
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dist;
    std::vector<int> v(10000000);
    for (auto &i: v)
        i = dist(gen);
    // run tests
    for (std::vector<int>::size_type x = 5; x <= 100; x+=5) {
        // keep this many values
        std::cout << x << " ";
        {
            stopclock t;
            auto result = filter_nth_element(v, x);
        }
        {
            stopclock t;
            auto result = filter_pqueue(v, x);
        }
        {
            stopclock t;
            auto result = filter_set(v, x);
        }
        {
            stopclock t;
            auto result = filter_deque(v, x);
        }
        {
            stopclock t;
            auto result = filter_vector(v, x);
        }
        std::cout << "\n";
    }
}

我发现看到这些方法的相对性能非常有趣(用-O3编译 - 我想我必须考虑一下这些结果):

comparison

答案 3 :(得分:0)

二叉搜索树可能是此问题的合适数据结构。也许你可以在STL或Boost中找到合适的Tree类(尝试寻找)。否则,如果你坚持,只需使用结构。

结构就像那样:

struct tnode {        /* the tree node: */
    char *word;           /* points to the text */
    int count;            /* number of occurrences */
    struct tnode *left;   /* left child */
    struct tnode *right;  /* right child */
};

摘自 C语言程序,第6.5章 - 自引用结构。只需根据您的需求进行调整。

尽管如此,我认为如果你想用C ++编程,请尝试创建一个Tree数据结构(类)或尝试使用现有的数据结构。

考虑到您只有300个条目,应该这样做。 理论上,当输入数据是 random 时,它应该是有效的。但这是理论,并没有真正发挥你的作用。我认为这是一个很好的解决方案。

答案 4 :(得分:0)

你可以使用5个元素的排序缓冲区,如果item高于缓冲区的最低项,则可以在每一步中使用,将项放入缓冲区并逐出

答案 5 :(得分:0)

使用let a = array(10) { arc4random_uniform(10) } print(a) // [3, 7, 9, 4, 2, 3, 1, 5, 9, 7] (Your output may be different :-) 个元素

首先创建一个类

map

如果最大的大小是&lt; 5,那么你还没有读过五个元素。

  class Data {
    public:
       std::string name;
       int         number;
};


 typedef std::map< int, Data > DataItems;
 DataItems  largest;

否则 - 如果它大于最大的五个中的最小值,那么最大的五个已经改变。

 if( largest.size() < 5 ) {
     largest[ dt.number] = dt;
 } else {

答案 6 :(得分:0)

您可以使用set来跟踪最高值。如果您想跟踪非唯一号码,请改用multiset

vector<int> nums{10,11,12,1,2,3,4,5,6,7,8,9}; //example data

int k=5;       // number of values to track
set<int> s;    // this set will hold the result

for(auto a: nums)
{
    if(s.size()<k)s.insert(a); 
    else if(a>*s.begin())
    {
       s.erase(s.begin());
       s.insert(a);
    }
}

当然,您必须为struct提供自定义比较功能