我如何对列表进行排序并获得前K个元素? (STL)

时间:2010-10-19 19:21:03

标签: c++ stl

我有一个双打矢量。我想从最高到最低排序,并获得前K个元素的索引。 std :: sort只是就地排序,并没有返回我认为的索引。什么是获得最大元素的前K个指数的快速方法?

6 个答案:

答案 0 :(得分:13)

你可以使用nth_element STL算法 - 这会返回N个最大的元素(这是最快的方法,使用stl),然后对它们使用.sort,或者你可以使用partial_sort算法,如果您希望对前K个元素进行排序(:

使用.sort非常糟糕 - 你想要的目的是非常慢.. .sort是很好的STL算法,但是对整个容器进行排序,而不仅仅是前K个元素(; nth_element和partial_sort的存在并非偶然;)

答案 1 :(得分:2)

首先想到的是有点hackish,但你可以定义一个存储double和它的原始索引的结构,然后重载<运算符基于double进行排序:

struct s {
    double d;
    int index;
    bool operator < (const struct &s) const {
        return d < s.d;
    }
};

然后你可以从结构中检索原始索引。

富勒示例:

vector<double> orig;
vector<s> v;
...
for (int i=0; i < orig.size(); ++i) {
    s s_temp;
    s_temp.d = orig[i];
    s_temp.index = i;
    v.push_back(s);
}
sort(v.begin(), v.end());
//now just retrieve v[i].index

这将使它们从最小到最大排序,但你可以重载&gt;而不是运算符,如果需要,然后传入更大的sort函数。

答案 2 :(得分:0)

不确定预先制作的算法,但请查看selection algorithms;如果你需要一组N个值的前K个元素,并且N远大于K,那么有更多有效的方法。

如果你可以创建一个索引类(比如@ user470379的答案 - 基本上是一个将指针/索引封装到只读的“真实”数据的类),那么使用最大大小为K的优先级队列,将每个未排序的元素添加到优先级队列,当队列达到大小K + 1时弹出最底部的元素。在N = 10 6 ,K = 100的情况下,这比完全排序更简单+有效地处理案例。

答案 3 :(得分:0)

好的,这个怎么样?

bool isSmaller (std::pair<double, int> x, std::pair<double, int> y)
{
   return x.first< y.first;
}

int main()
{
   //...
   //you have your vector<double> here, say name is d;
   std::vector<std::pair<double, int> > newVec(d.size());
   for(int i = 0; i < newVec.size(); ++i)
   {
      newVec[i].first = d[i];
      newVec[i].second = i;  //store the initial index
   }
   std::sort(newVec.begin(), newVec.end(), &isSmaller);
   //now you can iterate through first k elements and the second components will be the initial indices
}

答案 4 :(得分:0)

使用multimap vector(值,索引)来处理重复项。使用反向迭代器以降序排列结果。

#include <multimap>
#include <vector>
using namespace std;

multimap<double, size_t> indices;
vector<double> values;

values.push_back(1.0);
values.push_back(2.0);
values.push_back(3.0);
values.push_back(4.0);

size_t i = 0;
for(vector<double>::const_iterator iter = values.begin(); 
        iter != values.end(); ++iter, ++i)
{
    indices.insert(make_pair<double,int>(*iter, i));
}

i = 0;
size_t limit = 2;
for (multimap<double, size_t>::const_reverse_iterator iter = indices.rbegin(); 
    iter != indices.rend() && i < limit; ++iter, ++i)
{
    cout << "Value " << iter->first << " index " << iter->second << endl;
}

输出

  

价值4指数3

     

价值3指数2

如果您只想在排序后使用vector索引,请使用:

#include <algorithm>
#include <vector>
using namespace std;

vector<double> values;

values.push_back(1.0);
values.push_back(2.0);
values.push_back(3.0);
values.push_back(4.0);

sort(values.rbegin(), values.rend());

前K个条目的索引为0到K-1,并按降序显示。这使用反向迭代器与标准sort结合使用(使用less<double>在向前迭代时实现降序。等效:

sort(values.rbegin(), values.rend(), less<double>());

@Kiril建议的优秀nth_element解决方案的示例代码(K = 125000,N = 500000)。我想尝试一下,所以在这里。

vector<double> values;

for (size_t i = 0; i < 500000; ++i)
{
    values.push_back(rand());
}

nth_element(values.begin(), values.begin()+375000, values.end());
sort(values.begin()+375000, values.end());

vector<double> results(values.rbegin(), values.rbegin() + values.size() - 375000);

答案 5 :(得分:0)

所以你实际上需要一个将索引映射到相应双精度的结构。

您可以使用std::multimap类来执行此映射。正如Jason所说,std::map不允许重复密钥。

std::vector<double> v; // assume it is populated already
std::multimap<double, int> m;
for (int i = 0; i < v.size(); ++i)
    m.insert(std::make_pair(v[i], i));
...

完成此操作后,您可以迭代前十个元素,因为map会保留元素键的排序。