Сортування вектора у порядку зменшення у межах двох діапазонів


14

Скажіть, у мене є вектор цілих чисел:

std::vector<int> indices;
for (int i=0; i<15; i++) indices.push_back(i);

Потім я сортую це у порядку зменшення:

sort(indices.begin(), indices.end(), [](int first, int second) -> bool{return indices[first] > indices[second];})
for (int i=0; i<15; i++) printf("%i\n", indices[i]);

Це призводить до наступного:

14
13
12
11
10
9
8
7
6
5
4
3
2
1
0

Тепер я хочу, щоб цифри 3, 4, 5 і 6 були переміщені до кінця, і дотримуватись порядку їх спадання (бажано, не використовуючи sortвдруге). Тобто ось чого я хочу:

14
13
12
11
10
9
8
7
2
1
0
6
5
4
3

Як я можу змінити функцію порівняння std::sortдля досягнення цього?


4
return indices[first] > indices[second]Ви не маєте на увазі return first < second;?
acraig5075

2
Для простої сортування по спадаючій, std::greaterз <functional>можна використовувати замість вашого лямбда. Що стосується вашого запитання, написання більш детального порівняння, який забезпечує ваші значення порівняння так, як ви хочете, може бути найпростішим способом зробити це.
Soundsish

4
@ acraig5075, у порядку зменшення має бути return first > second.
ks1322

1
@ acraig5075 Я відчуваю, що мені щось не вистачає, чи люди не знають різниці між висхідним та низхідним ?
Soundsish

3
Можливо, ви шукаєте std :: rotate ?
супер

Відповіді:


8

Ваша функція порівняння неправильна, оскільки отримані значення є firstта secondє елементами std::vector. Тому не потрібно використовувати їх як індекси. Отже, потрібно змінити

return indices[first] > indices[second];

до

return first > second;

Тепер щодо проблеми, яку ви намагаєтеся вирішити ...

Ви можете залишити 3, 4, 5 і 6 порівняно з іншими елементами і все ж порівняти їх між собою:

std::sort(
    indices.begin(), indices.end(),
    [](int first, int second) -> bool {
        bool first_special = first >= 3 && first <= 6;
        bool second_special = second >= 3 && second <= 6;
        if (first_special != second_special)
            return second_special;
        else
            return first > second;
    }
);

Демо


@NutCracker Так, я погоджуюсь, що краще, щоб першим був критерій.
Купа переповнення

5

Функції з бібліотеки стандартних алгоритмів , як iota, sort, find, rotateі copyзроблять ваше життя простіше. Ваш приклад зводиться до:

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <iterator>


int main()
{
  std::vector<int> indices(15);
  std::iota(indices.begin(), indices.end(), 0);
  std::sort(indices.begin(), indices.end(), std::greater<>());

  auto a = std::find(indices.begin(), indices.end(), 6);
  auto b = std::find(indices.begin(), indices.end(), 3);
  std::rotate(a, b + 1, indices.end());

  std::copy(indices.begin(), indices.end(), std::ostream_iterator<int>(std::cout, "\n"));
  return 0;
}

Вихід:

14
13
12
11
10
9
8
7
2
1
0
6
5
4
3


@TedLyngmo в коментарях робить хорошим моментом, що його можна / слід вдосконалити за допомогою:

auto a = std::lower_bound(indices.begin(), indices.end(), 6, std::greater<int>{});
auto b = a + 4;

auto b = a + 4;помиляється (якщо ви хочете зберегти узгодженість з попереднім фрагментом). Це повинно бути auto b = a + 3;тому, що у std::rotateвикористанніb + 1
Biagio Festa

3

Рішення 1

Прямий підхід з нелінійним компаратором.

inline constexpr bool SpecialNumber(const int n) noexcept {
  return n < 7 && 2 < n;
}

void StrangeSortSol1(std::vector<int>* v) {
  std::sort(v->begin(), v->end(), [](const int a, const int b) noexcept {
    const bool aSpecial = SpecialNumber(a);
    const bool bSpecial = SpecialNumber(b);

    if (aSpecial && bSpecial) return b < a;
    if (aSpecial) return false;
    if (bSpecial) return true;
    return b < a;
  });
}

Рішення 2

Використовуючи std::algorithms (розділ)!

inline constexpr bool SpecialNumber(const int n) noexcept {
  return n < 7 && 2 < n;
}

void StrangeSortSol2(std::vector<int>* v) {
  auto pivot = std::partition(v->begin(), v->end(), std::not_fn(SpecialNumber));
  std::sort(v->begin(), pivot, std::greater{});
  std::sort(pivot, v->end(), std::greater{});
}

Розгляд продуктивності

Може здатися, що друге рішення повільніше через накладні перегородки. Напевно, це не так, через кеш і помилок передбачення у сучасних процесорах.

Орієнтир


Будь-який хороший компілятор повинен перетворитись n <= 6 && 3 <= n на все, що найкраще працює для цільового процесора, щоб ви нічого не отримували, вводячи числа 2 та 7, але потенційну плутанину - а навіщо брати вказівник на вектор замість посилання?
Тед Лінгмо

Не використовуйте "const int number" як функцію аргументу
Antoine Morrier

1
@AntoineMorrier Чому?
Купа переповнення

@HeapOverflow Тому що це те саме, не використовуючи const :).
Антуан Мор’єр

@AntoineMorrier Я не думаю, що це те саме. Чи не constговорить читачеві, що функція не змінює значення? У цьому конкретному випадку з одноклассником це може бути зрозуміло, але загалом це не так.
Купа переповнення
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.