【问题标题】:How to get maximum element in a vector that belongs to a range?如何获取属于某个范围的向量中的最大元素?
【发布时间】:2021-11-10 14:47:14
【问题描述】:

虽然有一个明确的 for 循环解决方案,但我一直在寻找一种解决方案来获得给定范围内向量中的最大值。

例如假设一个向量有3,5,7,9,5,1,6,7,4,2,给定的范围是[4,8],那么最大元素应该是7

我一直在寻找一种使用一些内置标准库函数的单行/短解决方案。

数组包含所有正数。

这是我尝试过的:

int max = -1;
for (i = 0; i < n; i++)
    if (arr[i] > max && arr[i] >= low && arr[i] <= high)
        max = arr[i];

我只是在寻找一个简单的标准库矢量解决方案。

【问题讨论】:

  • 您可以使用std::max_element()lambda 来检查当前值是否在范围内
  • 我认为 C++20 范围是一个不错的解决方案(排序 | equal_range | reverse | take(0)),但在 C++17 中,有问题的范围可能是最好的。
  • 范围内没有元素时应该返回什么? -1 ?
  • 你写它的方式很简单。您可以将其缩减为for (auto e : vec) if (e &gt; max &amp;&amp; e &gt;= low &amp;&amp; e &lt;= high) max = e;
  • 请注意,上面的算法与max_element略有不同:如果最大值有多个副本,则max_element返回第一个,但原始函数返回最后一个。 (在这种情况下不重要,因为这些值是整数,但如果它们更复杂,可能会变得重要。)

标签: c++ algorithm c++17 stdvector c++-standard-library


【解决方案1】:

这是一个利用两个标准库函数std::sortstd::find_first_of

#include <iostream>
#include <vector>
#include <tuple>
#include <algorithm>

// Find max element of sample vector within range of val_range.
using namespace std;

int main()
{
    vector<int> sample = { 3,5,7,9,5,1,6,7,4,2 };
    vector<int> val_range = { 4,8 };

    // Sort in reverse order
    sort(sample.begin(), sample.end(), greater<int>());
    vector<int>::iterator it = find_first_of(sample.begin(), sample.end(), val_range.begin(), val_range.end(), [](int value, int max_val) {return value <= max_val;} );
    cout << (*it) << endl;
}

See Live Demo

【讨论】:

    【解决方案2】:

    类似于@Yksisarvinen's answer,但使用std::tuple&lt; 来缩短它。

    前提条件是输入的范围内至少有一个元素。

    #include <cassert>
    #include <algorithm>
    #include <tuple>
    #include <vector>
    
    int main() {
        std::vector<int> arr {3,5,7,9,5,1,6,7,4,2};
        int lowerLimit = 4;
        int upperLimit = 8;
        
        auto inRange = [lowerLimit, upperLimit](int value)
        {
            return value >= lowerLimit && value <= upperLimit;
        };
        auto compare = [inRange](int lhs, int rhs) {
            return std::tuple(inRange(lhs), lhs) < std::tuple(inRange(rhs), rhs);
        };
        
        assert(!arr.empty());
        assert(std::any_of(arr.begin(), arr.end(), inRange));
        auto it = std::max_element(arr.begin(), arr.end(), compare);
        int max = *it;
    }
    

    【讨论】:

    • 这个给了我很多编译器错误,不能全部评论
    • @pujadeo 哎呀,已修复
    【解决方案3】:

    虽然其他人已经展示了使用标准算法的可行解决方案,但我想建议一种类似于 std::max_element 实现的解决方案。在这里,当您为算法函数提供无效范围时,您将获得更大的灵活性。

    #include <iterator> std::iterator_traits
    
    template<typename Iterator>
    using EleType = typename std::iterator_traits<Iterator>::value_type;
    
    template< class Iterator>
    auto my_max_element(Iterator first, const Iterator last,
        EleType<Iterator> minValue, EleType<Iterator> maxValue)
    {
        Iterator largest = first;
        for (; first != last; ++first)
            if (minValue <= *first && *first <= maxValue && *largest < *first)
                largest = first;
        // final range check for the largest element
        return (minValue <= *largest && *largest <= maxValue) ? largest : last;
    }
    

    (See a Live Demo)

    【讨论】:

    • 它应该忽略值 1、2、3 和 9,而不是向量中的索引。
    • 等待不,你没有得到我的问题,我的意思是向量本身内的数字应该在范围内过滤
    【解决方案4】:

    在一行中压缩太多或总是使用算法并不总是值得的,尤其是当循环清晰易读时。无论如何,如果您愿意,可以使用std::accumulate。二进制操作与您的原始代码非常接近,我只是使用条件运算符而不是 if:

    auto x = std::accumulate(arr.begin(),arr.end(),-1,[high,low](int accu,int elem){
        return (elem > accu && elem >= low && elem <= high ) ? elem : accu;
    });
    

    Live Demo

    【讨论】:

    • 如果它是结构对象的向量,这也可以工作
    • @pujadeo 当然,为什么不呢。为了清楚起见,我在这里使用了int 参数,但它们可能是auto(我相信从 C++14 开始)
    • @pujadeo 只要您为该类类型定义了比较,并且具有该类型的哨兵“最小”值
    • 我的意思是在这种情况下我需要迭代器到那个元素,但是accumulate会返回int
    • @pujadeo std::accumulate(arr.begin(),arr.end(),my_struct{},[high,low](my_struct accu, my_struct elem){ return (elem &gt; accu &amp;&amp; elem &gt;= low &amp;&amp; elem &lt;= high ) ? elem : accu; }); 返回my_struct
    【解决方案5】:

    这远非单行(除非您认为辅助函数不是代码的一部分,那么它肯定是一行),但是如果您真的想使用标准算法,您需要一个比较函数,它将符合严格的弱序。

    bool inRange(int value, int lowerLimit, int upperLimit)
    {
        return value >= lowerLimit && value <= upperLimit;
    }
    
    std::vector<int> arr {3,5,7,9,5,1,6,7,4,2};
        
    int lowerLimit = 4;
    int upperLimit = 8;
    auto compare = [lowerLimit, upperLimit](int l, int r){
        if(inRange(l, lowerLimit, upperLimit) && inRange(r, lowerLimit, upperLimit)) {
            // both values are in range, so compare them
            return l < r;
        } else if (inRange(r, lowerLimit, upperLimit)) {
            // r is in range, l is not, l should compare smaller
            return true;
        } else if (inRange(l, lowerLimit, upperLimit)) {
            // l is in range, r is not, r should compare smaller
            return false;
        } else {
            // both are out of range, compare them normally to keep strict weak ordering requirements
            return l < r;
        }
    };
    
    auto maxIter = std::max_element(std::begin(arr), std::end(arr), compare);
    std::cout << "Max value is " << *maxIter;
    

    【讨论】:

    • 你工作太辛苦了。传递给max_element的比较函数不需要是弱序。
    • @RaymondChen 如果前两个条件为假,我想我可以消除最后一个 else if 和始终 return false;,但我不能 100% 确定这是否适用于所有情况。
    • @RaymondChen doesn't it?
    • @Caleth 哦,我没有看到隐藏在那里的需求。偷偷摸摸。
    猜你喜欢
    • 1970-01-01
    • 2021-10-23
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    • 2022-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多