【问题标题】:std::max_element for second largest element?第二大元素的 std::max_element ?
【发布时间】:2015-08-09 21:36:36
【问题描述】:

STL 提供std::max_element 来查找可迭代对象中的最大元素,例如像这样:

std::vector<float>::const_iterator max = 
  std::max_element(obj.pt()->begin(), obj.pt()->end());
return std::distance(obj.pt()->begin(), max);

还有什么东西可以获取第 n 个最大元素的迭代器吗?

(请注意,max_element 返回一个迭代器,这实际上很重要:我正在寻找可迭代对象中第 n 个最大元素的位置,而不是值本身。)

【问题讨论】:

  • std::nth_element,虽然它正在修改序列。
  • @dyp 使用 std::sort 有什么不同吗?
  • 不能修改序列。
  • @lcjury, std::nth_element 是 O(n) 而不是 O(n log n)。
  • @fuenfundachtzig,如果你不介意 O(n) 空间,你可以创建一个副本并保持线性时间。不幸的是,这确实意味着另一个潜在的大副本分配。

标签: c++ stl


【解决方案1】:

max_element() 方法可用于通过传递 lambda 函数来获取第二大元素,该函数将该元素与先前找到的最大元素进行比较,如果它等于最大元素,那么它将简单地跳过该元素。

    auto largest = max_element(vec.begin(), vec.end());
    auto secondLargest = max_element(vec.begin(), vec.end(),
                                     [&largest](unsigned long &a, unsigned long &b) {
                                         if (a == *largest) return true;
                                         if (b == *largest) return false;                                         
                                         return a < b;
                                     });

【讨论】:

    【解决方案2】:

    如果您对第二大元素特别感兴趣,您可以对数组进行简单扫描,其中大多数元素都需要进行一次比较:

    float second_largest_element(std::vector<float> vec) {
      float m2, m1;
      /* Check to make sure that vec has at least 2 elements!! */
      std::tie(m2, m1) = std::minmax(vec[0], vec[1]);
      for (auto it = vec.begin() + 2, limit = vec.end();
           it != limit;
           ++it)
        if (*it > m2) std::tie(m2, m1) = std::minmax(*it, m1);
      return m2;
    }
    

    获取第二大元素的索引(或迭代器)非常相似,尽管std::minmax 用处不大。这是一个非常草率的例子:

    template<typename T>
    typename T::iterator second_largest(T& container) {
      using iterator = typename T::iterator;
      iterator limit = container.end();
      iterator it = container.begin();
      if (it != limit) {
        iterator first = it++;
        if (it != limit) {
          iterator second = it++;
          if (*first < *second) std::swap(first, second);
          for (; it != limit; ++it) {
            if (*second < *it) {
              if (*first < *it) { second = first; first = it; }
              else              { second = it; }
            }
          }
          return second;
        }
        return first;
      }
      return it;
    }
    

    您也可以考虑使用std::accumulate 来扫描数组,虽然显式的 for 循环并不复杂。

    【讨论】:

      【解决方案3】:

      正如 Dyp 在评论中提到的,如果您可以更改 vector 中元素的顺序,您可以使用 std::nth_element,如下所示。最重要的是,如果您再次使用find 而不是vector,您将从vector 获得第n 个元素的原始位置。由于nth_element 修改了位置,因此在对向量进行nth_element 操作之前,您必须保留它的本地副本。

      第二大元素:

      std::vector<float> orig_vec=obj.pt;
      
      std::nth_element(obj.pt().begin(), obj.pt().begin()+1, 
                        obj.pt().end(), std::greater<float>());
      float 2nd= *(obj.pt().begin()+1);
      auto it=std::find(orig_vec.begin(), orig_vec.end(), 2nd);
      

      第n大元素:

        std::nth_element(obj.pt().begin(), obj.pt().begin()+n-1,
          obj.pt().end(), std::greater<float>());
        float nth= *(obj.pt().begin()+n-1);
      
       auto it=std::find(orig_vec.begin(), orig_vec.end(), nth)
      

      【讨论】:

      • 但这不会让我获得原始向量中第n个元素的位置,对吧?
      • @fuenfundachtzig 我编辑了答案以从原始vector获得第n个位置
      • 我有一个反对意见:根据cppreference,nth_element 没有返回值(或者根本不返回任何值),因此您确实需要先复制向量。
      • @Paul 你的红旗是对的,我更正了答案。谢谢
      【解决方案4】:

      这是一种在线性时间内实现的简单算法。天真的方法是比较前两个值,并选择它们作为最大值和第二大值。然后,您需要遍历其他元素,将每个新元素与它们两者进行比较,并调整当前的最大值和第二最大值。对于大多数用例来说,这可能绰绰有余。如果您真的关心性能(因为您非常关心很多),您将需要考虑要比较哪些值以尽量减少比较次数。

      另请注意,float(一般为浮点)有一些怪癖...如果您的输入包含 NaN 或无限值,您可能会得到有趣的值。

      【讨论】:

      • 我更新了问题,但您的回答仍然有效。
      【解决方案5】:

      我花了一段时间才找到解决方案,因为我使用的是 const vector(所以我不能使用 nth_element),而复制只是浪费(尤其是当 vector 持有更大的结构时)。所以我带着这个:

      // Find 1st max
      auto max1 = max_element(vec.begin(), vec.end());
      if (max1 != vec.end())
          // Use max1
      
      // Find 2nd max. Split the vector into 2 parts, find max and merge results
      auto max2Beg = max_element(vec.begin(), max1);
      auto max2End = max_element(max1 + 1, vec.end());
      auto max2 = max2Beg == max1 ? max2End :
                  max2End == vec.end() ? max2Beg : max(max2Beg, max2End);
      if (max2 != max1 && max2 != vec.end())
          // Use max2
      

      【讨论】:

      • 一件重要的事情:您需要为max_element() 定义谓词,例如auto predicateRef = [](const auto&amp; lhcs, const auto&amp; rhcs) { return lhcs.getValue() &lt; rhcs.getValue(); },为max() auto predicatePtr = [](const auto* lhcs, const auto* rhcs) { return lhcs-&gt;getValue() &lt; rhcs-&gt;getValue(); } 定义另一个谓词。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-27
      相关资源
      最近更新 更多