Заставить set сортироваться по убыванию

156
22 июля 2019, 12:10

Как известно, set - множество в С++, которое отсортировано по возрастанию. Мне известно, что можно сделать так, чтобы set был отсортирован по убыванию. Одним словом цель - добиться, чтобы set сортировался по убыванию. Кто знает, как это сделать?

Answer 1

Воспользоваться готовым средством - заменить less на greater, типа

set<int,greater<int>> s { 2,5,8,1,4,9};
for(auto i: s) cout << i << endl;

set - не "множество в с++ которое отсортировано по возрастанию", оно сортируется так, как ему указывают. По умолчанию - с помощью компаратора less<>. А так - сортируйте, как хотите.

Answer 2

Контейнер std::set всегда отсортирован "по возрастанию". Вы можете повлиять лишь на то, что такой контейнер считает "возрастанием".

Каким образом будет упорядочен std::set определяется предикатом сравнения элементов, тип которого является вторым параметром шаблона std::set и значение которого является параметром конструктора std::set. По умолчанию в качестве предиката сравнения используется std::less. Стандартная реализация std::less внутри себя выполняет сравнение путем обращения к оператору < для сравниваемых элементов.

Это дает вам три возможных "точки входа" в механизм сравнения, три способа воздействовать на порядок упорядочения в std::set и других аналогичных контейнерах:

  1. Использовать другой предикат сравнения в качестве параметра std::set

    #include <set>
    #include <iostream>
    struct MyType
    {
      int i;
      friend bool operator <(const MyType &lhs, const MyType &rhs)
        { return lhs.i < rhs.i; };
      friend bool operator >(const MyType &lhs, const MyType &rhs)
        { return lhs.i > rhs.i; };
    };
    int main()
    {
      std::set<MyType, std::greater<MyType>> my_set = 
        { { 2 }, { 8 }, { 5 }, { 4 }, { 1 }, { 3 } };
      for (auto &e : my_set)
        std::cout << e.i << " ";
      std::cout << std::endl;
    }
    

    http://coliru.stacked-crooked.com/a/cccfbce2e58cbfc8

    Как сказано выше, значение этого предиката тоже учитывается контейнером - оно передается в контейнер через параметр конструктора. Хоть это и редко используемая возможность, но этот предикат можно наделить состоянием и решение о его поведении принимать во время выполнения

    #include <set>
    #include <iostream>
    struct MyType
    {
      int i;
      friend bool operator <(const MyType &lhs, const MyType &rhs)
        { return lhs.i < rhs.i; };
      friend bool operator >(const MyType &lhs, const MyType &rhs)
        { return lhs.i > rhs.i; };
    };
    struct MyCmp
    {
      bool ascending = true;
      bool operator ()(const MyType &lhs, const MyType &rhs) const
      {
        return ascending ? lhs < rhs : lhs > rhs;
      }
    };
    int main()
    {
      std::set<MyType, MyCmp> 
        my_set_a({ { 2 }, { 8 }, { 5 }, { 4 }, { 1 }, { 3 } }, { true });
      for (auto &e : my_set_a)
        std::cout << e.i << " ";
      std::cout << std::endl;
      std::set<MyType, MyCmp> 
        my_set_d({ { 2 }, { 8 }, { 5 }, { 4 }, { 1 }, { 3 } }, { false });
      for (auto &e : my_set_d)
        std::cout << e.i << " ";
      std::cout << std::endl;
    }
    

    http://coliru.stacked-crooked.com/a/86f98e157467a3dd

  2. Определить специализацию std::less для типа элемента контейнера (только если элемент std::set имеет пользовательский тип)

    #include <set>
    #include <iostream>
    struct MyType
    {
      int i;
      friend bool operator <(const MyType &lhs, const MyType &rhs)
        { return lhs.i < rhs.i; };
      friend bool operator >(const MyType &lhs, const MyType &rhs)
        { return lhs.i > rhs.i; };
    };
    template<> struct std::less<MyType>
    {
      bool operator ()(const MyType &lhs, const MyType &rhs) const
        { return lhs > rhs; }
    };
    int main()
    {
      std::set<MyType> my_set = { { 2 }, { 8 }, { 5 }, { 4 }, { 1 }, { 3 } };
      for (auto &e : my_set)
        std::cout << e.i << " ";
      std::cout << std::endl;
    }
    

    http://coliru.stacked-crooked.com/a/2c7f508d7235e7b1

  3. Определить свою версию оператора < для типа элемента контейнера (только если элемент std::set имеет пользовательский тип)

    #include <set>
    #include <iostream>
    struct MyType
    {
      int i;
      friend bool operator <(const MyType &lhs, const MyType &rhs)
        { return lhs.i > rhs.i; };
    };
    int main()
    {
      std::set<MyType> my_set = { { 2 }, { 8 }, { 5 }, { 4 }, { 1 }, { 3 } };
      for (auto &e : my_set)
        std::cout << e.i << " ";
      std::cout << std::endl;
    }
    

    http://coliru.stacked-crooked.com/a/481288ec2ffc4e55

Второй вариант не является рекомендуемым, ибо его поведение может быть "неожиданным" для неподготовленного читателя кода. Обычно приходится выбирать между первым и третьим вариантом, в зависимости от того, является ли требуемое вами упорядочение наиболее естественным для типа элемента контейнера.

Answer 3

Как альтернативу решению выше, могу предложить небольшую хитрость: чтобы set сортировался по убыванию, вы можете сделать всё числа отрицательными. Таким образом, они тоже отсортируются по убыванию.

Например:

s.insert(x) становится s.insert(-x).

*(s.begin()) становится -(*(s.begin()))

READ ALSO
Не получается назначить права на файл

Не получается назначить права на файл

Не получается назначить права на файлПрошу помощи!!!:

157
нужна помощь с кодом типа Random на java

нужна помощь с кодом типа Random на java

Я сам осваивать джава стал недавно, так что прошу простить за может тупой или неправильный вопросЯ хочу, чтоб после того, как рандомайзер...

176
Метод print() класса PrintStream

Метод print() класса PrintStream

Дорогие камрады! :)

145
Объект события в java

Объект события в java

Для чего нужен объект события?(аргумент реализуемого метода интерфейса)

150