【问题标题】:Find second (or n) highest value of a field in a vector of object c++在对象c ++的向量中查找字段的第二(或n)最大值
【发布时间】:2014-11-13 16:41:36
【问题描述】:

related question 中,我询问了如何根据所述对象的某些字段在 c++ 中的对象向量中获取最大元素的值。我扩展了算法来获取那个最大元素的索引,这样我以后就可以从对象的向量中弹出它。

这是我现在使用的代码,它运行良好:

vector<MyOwnClass> scoretracker // MyOwnClass has a ".score" field

// Some code filling scoretracker

auto max = std::max_element(scoretracker.begin(), scoretracker.end(),
    [] (const MyOwnClass &a, const MyOwnClass &b )
{
    return a.score < b.score; // I am not sure how this line works
});

int index = distance(scoretracker.begin(), max);

所以,我尝试修改它以获得 第二高(或第 n 个值)而不是最大值,但到目前为止我的尝试失败了。

这让我意识到我真的不明白为什么“return a.score

通过查看 max_element works 的方式,我不确定它是否可以用来找到第二大的。

哦,最后,我宁愿不从向量中pop_back最高值,找到新的最大值(原始向量中的第二个最大值)并添加一些逻辑来恢复原始版本。好吧,如果我必须这样做,我会这样做,但可能会有一些迭代器属性或其他我不知道的东西......

【问题讨论】:

  • 您可以只使用std::sort,而不是max_element。然后你可以通过索引排序的数组(或者如果你想要的排序副本)来找到第 1 个、第 2 个或第 n 个最高元素。
  • 好的,我现在正在尝试一些实现!
  • why "return a.score &lt; b.score" returns the highest value 这是对值进行排序的谓词。 值排序之后做什么取决于算法。
  • @Cyber​​ 好的,我错过了您评论中的最后几句话。如果不想破坏向量的顺序,只需对副本进行排序。我会看看它在我的过程中吃了多少时间。谢谢!
  • @PaulMcKenzie 感谢您的精确! :)

标签: c++ object vector iterator field


【解决方案1】:

如果您只需要一次的索引(而不是所有值),您可以使用:

std::size_t get_index_of_nth_greatest(const std::vector<MyOwnClass>& v, std::size_t k)
{
    std::vector<std::size_t> indexes(v.size());
    std::iota(indexes.begin(), indexes.end(), 0);

    std::nth_element(indexes.begin(), indexes.begin() + k, indexes.end(),
        [&](int lhs, int rhs)
        {
            return v[lhs].score > v[rhs].score;
        }
    );
    return indexes[k];
}

Live example.

注意:正如来自莫斯科的 Vlad 指出的那样,对于重复的输入,无法保证重复的顺序,因此对于不同的输入,您可能具有相同的索引k.

【讨论】:

  • 我知道partial_sort,所以我认为一定存在类似于nth_element的东西。
  • @Jarod42 这是一个错误的解决方案,因为通常情况下容器的元素可以重合。
  • @VladfromMoscow:相同的索引可能对多个k(有重复)有效,即使它不直观(顺便说一句,我在这个example 中有不同的索引)。
  • @Jarod42 我不明白你写了什么,但是请回答序列 { 2, 2, 2, 1 } 和 k 等于 2 将返回什么?
  • @VladfromMoscow:仅对于k = 3,预期索引将为3,值为=1。对于 k {0, 1, 2} 中的任何索引对我来说都是可以接受的(值为 =2)。
【解决方案2】:

试试这个:

auto max = std::sort(scoretracker.begin(), scoretracker.end(),
    [] (const MyOwnClass &a, const MyOwnClass &b )
{
    return a.score < b.score;
});

然后

scoretracker.back().score;

会给你最后一个元素

scoretracker.at(position).score;

将返回位置可以是任意数字的位置的元素

【讨论】:

  • 他不必对整个序列进行排序以获得第 n 个最高元素,std::nth_element 就足够了
  • 天哪,有货吗??哇哦,我不知道什么时候才能看到现在 c++ 提供的一切。顺便说一句,谢谢@Slava
  • 由于这个 (std::sort) 在我的情况下有效并且我无法正确使用 nth_element,我最终会将其标记为正确答案。但是如果有人向我展示了一个带有 nth_element 的工作示例并且它在我的代码中更快,我会选择最快的 ^^
【解决方案3】:

如果您想要更复杂的方法,可以查看std::partition 函数。

std::partition 获取一个容器并将其分成两部分。

std::vector<MyOwnClass> v(100);
// fill vector with stuff. Find n-th element.
auto mid = v[somwhere_in_the_middle];
auto p = std::partition(v.begin(), v.end(), 
                [mid](MyOwnClass v){ return v.score < mid.score; } );

每个大于p 的元素都在p 的右侧。较小的在左边。 如果要找第二大的,只要v.end() - p的距离够大就往右走。

这个方法叫做快速选择,基于快速排序的思想,这里有详细介绍How to find the kth largest element in an unsorted array of length n in O(n)?

这当然已经实现为std::nth_element,可以用作

std::nth_element(v.begin(), v.begin()+5, v.end(), std::greater<int>());

在第 6 位获得第 6 大的元素。

【讨论】:

  • 很好地阅读了 nth_element,这听起来像是我想要的。据我了解,+5 表示所需物品的位置(从 0 开始计数)。 auto p = std::nth_element(scoretracker.begin(), scoretracker.begin()+1, scoretracker.end(), std::greater&lt;double&gt;() ); 奇怪的是,有一个错误说std中没有成员“greater”,这似乎不是真的(VS2012Pro,C++)
  • 好的,我应该添加#include stackoverflow.com/questions/16567699/…
  • @Doombot 是的,您需要为其提供与您当前使用的相同的比较 lambda。它不知道您的分数成员。也包括
  • 好的,分区代码中的somwhere_in_the_middle 怎么样,无论我使用什么值,它都会将相同的东西返回到 p 中。另外,我尝试以不同的方式拟合比较 lambda,但都失败了。所以这意味着我不理解 lambda... -_-
  • 好的,现在我使用了这个:std::nth_element(scoretracker.begin(), scoretracker.begin()+1, scoretracker.end(), [] (const MyOwnClass &amp;a, const MyOwnClass &amp;b ) { return a.score &gt; b.score; });,它会将 scoretracker 从最高分到最低分进行排序。没关系,但就像我使用排序一样?
【解决方案4】:

您可以通过获取已有的 max 元素并在其任一侧查找 std::max_element() 并获取这两个值的 std::max() 来做到这一点:

struct MyOwnClass
{
    int score = 0;
    MyOwnClass(int s): score(s) {}
};

vector<MyOwnClass> scores = {{4}, {2}, {7}, {9}, {3}}; 

int main()
{
    // we need this more than once
    auto compare = [] (const MyOwnClass &a, const MyOwnClass &b)
    {
        return a.score < b.score; // I am not sure how this line works
    };

    auto max = std::max_element(scores.begin(), scores.end(), compare);

    std::cout << "max: " << max->score << '\n';

    // call std::max_element() twice with ranges either side of the max element
    // and get the std::max() of those values
    auto next_max = std::max(std::max_element(scores.begin(), max, compare)->score
        , std::max_element(max + 1, scores.end(), compare)->score);

    std::cout << "next max: " << next_max << '\n';
}

注意:如果你想获得 thirdfourth 元素,这种技术是一种收益递减技术,可能最好做std::sort().

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-06
    • 2011-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    相关资源
    最近更新 更多