【问题标题】:Getting the n best elements from a vector?从向量中获取 n 个最佳元素?
【发布时间】:2015-03-27 20:43:37
【问题描述】:

我有一个结构向量,为此我重载了所有比较运算符。我不知道编译时结构的大小。

检索向量中 n 个最佳(其中“最佳”可以是最小或最大)元素的最快方法是什么?我知道 max_element 和 min_element 但它们只返回一个元素。我宁愿不循环 n 次,检索最佳元素,删除它,然后获取下一个。这种方法似乎太慢了。

谢谢。

【问题讨论】:

  • 对向量进行排序然后读取顶部和底部的 N 元素?
  • std::partial_sort 甚至更好的std::nth_element

标签: c++ vector data-structures stl


【解决方案1】:

您可以使用std::nth_element,它将最小的n 元素移动到范围内的第一个n 位置(请注意,这些n 元素的相对顺序未定义)。

std::vector<T> objects;
std::nth_element( objects.begin(), objects.begin() + n, objects.end() );

// Now the range [objects.begin(), objects.begin() + n) contains the lowest n elements
// Obviously n must be <= objects.size()

当你在编译时不知道结构的大小时,我假设你有一个多态对象的集合,并且你有一个指针向量而不是元素。没什么大不了的,您仍然可以将 std::nth_element 与 lambda 一起使用。

std::vector<T*> objects;
std::nth_element( objects.begin(),
                  objects.begin() + n,
                  objects.end(),
                  [](const T * lhs, const T * rhs)
                  {
                      return (*lhs) < (*rhs); // Or (*lhs) > (*rhs) for the greatest n elements
                  });

【讨论】:

    【解决方案2】:

    对其进行排序并取 n 个第一个元素。

    struct Foo { };
    std::vector<Foo> v = { Foo(), Foo(), Foo() };
    std::sort(v.begin(), v.end());
    
    std::vector<Foo> v_best(v.begin(), v.begin() + n); //where n is your best count
    

    【讨论】:

      【解决方案3】:

      另一种方法比蛮力排序和优先排序:设置大小为n 的临时容器,并使用原始向量的第一个n 元素对其进行初始化(大小为@987654323 @, 说)。对该临时向量进行排序。

      接下来遍历您的原始向量并插入元素(使用std::lower_bound),只要它们大于已排序容器的当前最小值(同时丢弃当前最小值)。这应该首先渐近地采用O(n log n + N) 而不是O(N log N),但更重要的是,常量前置因子也可能要小得多。

      【讨论】:

        【解决方案4】:

        如果要检索n 最小的元素,请按升序对vector 进行排序。

        如果要检索n 的最大元素,请按降序对vector 进行排序。

        那么,

        1. 获取两个迭代器。

          start = v.begin();
          end = start + n;
          
        2. start 迭代到end,但不包括end

        【讨论】:

        • 如果不想排序?
        【解决方案5】:

        我建议您既不修改也不复制矢量的解决方案

        所以我假设原始向量非常非常大,您仍然需要原始顺序的数据用于其他目的

        template <typename T>
        set<T> best_n2(vector<T>& v, size_t n)
        {
            set<int> u;          // the result is a sorted set
            int i = 0;
            for (auto x : v) {   // fill the N first elements
                if (i++ < n) {
                    u.insert(x);
                }
                else if (x > *u.begin()) {  // then get rid of the smallest if necessray
                    u.erase(*u.begin()); 
                    u.insert(x);
                }
            }
            return u;
        }
        

        如果你可以修改原始向量,我会选择 szulack 的解决方案。我没有提议,因为他打字比我快;-)

        【讨论】:

          【解决方案6】:

          此处的其他答案有效,但您也可以使用copy_if

          std::vector<int> vec{ 1, 5, 7, 9, 3, 6, 8, 4, 2 };
          
          std::vector<int> odd;
          std::copy_if(vec.begin(), vec.end(), std::back_inserter(odd), [](int a){ return a % 2 == 1; });
          std::vector<int> low;
          std::copy_if(vec.begin(), vec.end(), std::back_inserter(low), [](int a){ return a <= 3; });
          std::vector<int> high;
          std::copy_if(vec.begin(), vec.end(), std::back_inserter(high), [](int a){ return a >= 7; });
          

          如果需要删除匹配数据,还有remove_copy_if

          【讨论】:

          • 对不起,我没听懂。当您事先不知道值时,这如何复制 10 个最佳元素?
          • copy_if 将给定范围内的值复制到指定的向量中,并接受要复制的内容的谓词(我在此以 lambda 表达式的形式传入该谓词)。因此,您只需定义“最佳”含义的谓词(函数)即可。
          • 这不是前 n 个好的,而是 n 个最好的。您能否向我们展示您将用来选择 10 个最大元素的 lamda 吗?
          • @Christophe 你是对的,现在看看这个,这不是他想要的一个很好的解决方案。当我阅读他的问题时,我将“最佳”理解为未定义,而 min 和 max 更像是一个例子。
          猜你喜欢
          • 2017-08-31
          • 1970-01-01
          • 1970-01-01
          • 2018-12-03
          • 2015-01-11
          • 1970-01-01
          • 2011-05-12
          • 2013-08-29
          • 1970-01-01
          相关资源
          最近更新 更多