【问题标题】:Sort objects in descending order when the < comparator is defined?定义 < 比较器时按降序对对象进行排序?
【发布时间】:2013-05-09 03:33:17
【问题描述】:

我有一个类A 和一个&lt; 比较器。如何使用它们以降序顺序对A 数组进行排序?

class A {
...
};

class LessA {
   bool operator()(const A& a1, const A& a2) const {
   ...
   }
}

vector<A> v;
sort(v.begin(), v.end(), ???);

我想我应该用基于LessA 的东西替换???,但我不知道应该放什么。我想使用 lambda 函数,但我正在寻找更短的东西。

【问题讨论】:

标签: c++ sorting c++11 comparator


【解决方案1】:

如果您想根据 LessA 比较器定义的关系进行排序,只需将 LessA 的实例作为第三个参数传递(并且,由于您使用的是 C++11,因此更喜欢全局 std::begin()std::end()函数):

std::sort(std::begin(a), std::end(a), LessA());
//                                    ^^^^^^^

现在,如果您的LessA() 表达了&lt; 关系并且您想根据相反的标准进行排序,您可以这样做:

std::sort(std::begin(a), std::end(a), 
    [] (A const& a1, A const& a2))
{
    return LessA()(a2, a1);
}

您可以做的另一件事是让您的自定义比较器接受一个参数来确定它应该如何执行比较:

class CompA {
    bool lessThan;
public:
    CompA(bool lessThan) : _lessThan(lessThan) { }
    bool operator()(const A& a1, const A& a2) const {
        if (_lessThan)
        {
            // return true iff a1 < a2;
        }
        else
        {
            // return true iff a1 > a2;
        }
    }
};

然后您可以使用这种方式按升序排序:

std::sort(std::begin(a), std::end(a), CompA(true));

而这种方式按降序排序:

std::sort(std::begin(a), std::end(a), CompA(false));

鉴于您原来的 LessA 比较器,另一种可能性是使用 std::bind 将参数的顺序交换到您的自定义比较器:

LessA comp;
using namespace std::placeholders;
std::sort(std::begin(v), std::end(v), 
    std::bind(&LessA::operator(), comp, _2, _1));

【讨论】:

  • 是的,但这会按升序对v 进行排序。我希望它按降序排列。
  • @PaulBaltescu 只需翻转您的比较结果。
  • @Magtheridon96:没有,但std::begin() 更通用(它也适用于 C 数组)
  • @AndyProwl:当a &gt; b 等同于b &lt; a 时,为什么要使用!=
  • 我想例如std::bind(LessA(), _2, _1) 是更自然的绑定表达式。
【解决方案2】:

使LessA 类的() 运算符返回!(a1 &lt; a2) 并像这样传入:

std::sort(v.begin(), v.end(), LessA());

【讨论】:

  • 是的,但这会按升序对v 进行排序。我希望它按降序排列。
  • !(a1 &lt; a2) 会让你变得更好,我想你想要(a2 &lt; a1) 代替
  • 我只是否定了他告诉我的条件是给他倒退的结果并实现了这一点。坦率地说,我不知道哪个会产生正确的结果:p
  • @Magtheridon96 一般来说(也有例外),!(a &lt; b)相当于a &gt;= b。您应该否定假设的a &lt;=&gt; b 操作的结果:之前比较为相等的两个项目应该继续比较为相等(两者都比较不小于另一个)。
【解决方案3】:

将范围向后排序:

vector<A> v;
sort(v.rbegin(), v.rend(), LessA());

rbeginrend 为您提供反向迭代器。

如果太混乱,请封装:

void reverse_sort(vector<A>& v) {
    sort(v.rbegin(), v.rend(), LessA());    
}

用法:

vector<A> v;
reverse_sort(v);

【讨论】:

  • @Xeo 我能说什么,你有 30 票赞成说我的反对意见。 30 位支持者不会错(c:
  • 我并不是要使用赞成票作为论据,我只是将我的回答作为对我为什么不喜欢这个版本的扩展解释。对不起,如果它看起来像前者。
  • @Xeo 我很感激(c:我已经封装了函数调用,如果这能让它变得更甜的话。
  • 我喜欢这个。它并不比特殊的反向比较类型、lambda 或 bind 复杂。是否可以使用std::greater 会更好,但 IMO 在这里使用反向范围是合理的。
【解决方案4】:

使用std::greater 作为比较函子。默认(std::less)会给你一个升序;这会给你一个降序。 (您需要添加 using namespace std::rel_ops; (link) 语句或明确定义 operator&gt;。)

示例

取自cppreference.com

#include <algorithm>
#include <functional>
#include <array>
#include <iostream>
 
int main()
{
    std::array<int, 10> s = {5, 7, 4, 2, 8, 6, 1, 9, 0, 3}; 
 
    // sort using the default operator<
    std::sort(s.begin(), s.end());
    for (int a : s) {
        std::cout << a << " ";
    }   
    std::cout << '\n';
 
    // sort using a standard library compare function
    std::sort(s.begin(), s.end(), std::greater<int>());
    for (int a : s) {
        std::cout << a << " ";
    }   
    std::cout << '\n';
 
    // sort using a custom functor
    struct {
        bool operator()(int a, int b)
        {   
            return a < b;
        }   
    } customLess;
    std::sort(s.begin(), s.end(), customLess);
    for (int a : s) {
        std::cout << a << " ";
    }   
    std::cout << '\n';
 
    // sort using a lambda
    std::sort(s.begin(), s.end(), [](int a, int b) {
        return b < a;   
    });
    for (int a : s) {
        std::cout << a << " ";
    } 
    std::cout << '\n';
}

【讨论】:

  • 如何将greaterA 类型的对象结合使用?
  • @PaulBaltescu 你应该为你的班级A 重载operator&lt;,然后如果你使用std::greater&lt;A&gt;,一切都会自动运行。
  • @PaulBaltescu 我刚刚进行了编辑。请参阅我的答案中的第二段,了解您可以使用的替代方案。
  • @TimothyShields,std::greater 使用 operator&gt;,它不适用于仅定义 operator&lt; 的类,并且否定 less 大于或等于,因此不是 StrictWeakOrdering .
  • @JonathanWakely 我认为这是标准的(使用 rel_ops link)?如果不是,那么是的,您还必须明确定义 operator&gt;
【解决方案5】:

给定一个实现a&lt;b 的函数lt(a, b),您可以通过返回!lt(a, b) 创建一个实现a&gt;=b 的函数。要实现&gt;,需要返回!lt(b, a) &amp;&amp; !(lt(a,b) || lt(b,a))

lt(a, b) || lt(b, a) 等价于a!=b,所以上面等价于a&gt;=b &amp;&amp; a!=b 减少为a&gt;b

但是,您可能只需要std::not2(LessA()) 就可以逃脱惩罚。这将使用&gt;= 进行排序,并按降序排序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-21
    • 2014-07-24
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 2020-02-19
    • 2013-08-16
    • 2023-04-06
    相关资源
    最近更新 更多