Ordinamento di un vettore in ordine decrescente in due intervalli


14

Di 'che ho un vettore di numeri interi:

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

Quindi lo ordino in ordine decrescente:

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]);

Questo produce quanto segue:

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

Ora voglio avere i numeri 3, 4, 5 e 6 da spostare fino alla fine e mantenere l'ordine discendente per loro (preferibilmente senza doverlo usare sortper la seconda volta). Cioè, ecco quello che voglio:

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

Come devo modificare la funzione di confronto di std::sortper ottenere ciò?


4
return indices[first] > indices[second]Non intendi return first < second;?
acraig5075,

2
Per un semplice ordinamento decrescente, std::greaterdal <functional>può essere usato al posto del tuo lambda. Per quanto riguarda la tua domanda, scrivere un comparatore più dettagliato che assicuri che i tuoi valori vengano confrontati nel modo desiderato potrebbe essere il modo più semplice per farlo.
svedese

4
@ acraig5075, in ordine decrescente dovrebbe essere return first > second.
ks1322,

1
@ acraig5075 Sento che mi manca qualcosa o le persone non conoscono la differenza tra ascendente e discendente ?
svedese

3
Forse stai cercando std :: rotate ?
super

Risposte:


8

La tua funzione di confronto è sbagliata poiché i valori che ottieni come firste secondsono gli elementi di std::vector. Pertanto, non è necessario utilizzarli come indici. Quindi, devi cambiare

return indices[first] > indices[second];

per

return first > second;

Ora, riguardo al problema che provi a risolvere ...

Puoi lasciare 3, 4, 5 e 6 fuori dal confronto con altri elementi e comunque confrontarli tra loro:

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;
    }
);

dimostrazione


@NutCracker Sì, sono d'accordo che è meglio avere prima il criterio principale.
Heap Overflow

5

Funzioni dalla libreria di algoritmi standard come iota, sort, find, rotatee copyrenderebbe la vita più facile. Il tuo esempio si riduce a:

#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;
}

Produzione:

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


@TedLyngmo nei commenti sottolinea che potrebbe / dovrebbe essere migliorato con:

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

auto b = a + 4;è errato (se si desidera mantenere la coerenza con lo snippet precedente). Dovrebbe essere auto b = a + 3;perché nel std::rotatetuo usob + 1
Biagio Festa

3

Soluzione 1

Approccio diretto con un comparatore non lineare .

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;
  });
}

Soluzione 2

Usando std::algorithms (partizione)!

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{});
}

Considerazioni sulle prestazioni

Potrebbe sembrare che la seconda soluzione sia più lenta a causa del sovraccarico della partizione. Probabilmente no, a causa della previsione cache e miss-branch nei processori moderni.

Prova delle prestazioni


Qualsiasi buon compilatore dovrebbe trasformarsi n <= 6 && 3 <= n in ciò che funziona meglio per la CPU di destinazione in modo da non ottenere nulla introducendo i numeri 2 e 7 ma la potenziale confusione - e perché prendere un puntatore al vettore anziché un riferimento?
Ted Lyngmo,

Non usare `const int number` come funzione argomento
Antoine Morrier,

1
@AntoineMorrier Perché?
Heap Overflow

@HeapOverflow Perché è lo stesso senza usare const :).
Antoine Morrier,

@AntoineMorrier Non penso sia lo stesso. Non constdice al lettore che la funzione non cambia il valore? In questo caso particolare di una linea potrebbe essere chiaro, ma in generale non lo è.
Heap Overflow
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.