Tri d'un vecteur par ordre décroissant dans deux plages

14

Disons que j'ai un vecteur d'entiers:

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

Ensuite, je le trie par ordre décroissant:

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

Cela produit les éléments suivants:

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

Maintenant, je veux que les chiffres 3, 4, 5 et 6 soient déplacés à la fin et garder l'ordre décroissant pour eux (de préférence sans avoir à utiliser sortpour la deuxième fois). C'est à dire, voici ce que je veux:

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

Comment dois-je modifier la fonction de comparaison du std::sortpour y parvenir?

Yury
la source
4
return indices[first] > indices[second]Tu ne veux pas dire return first < second;?
acraig5075
2
Pour une sorte simple descendante, std::greaterde <functional>peut être utilisé à la place de votre lambda. En ce qui concerne votre question, l'écriture d'un comparateur plus détaillé qui garantit que vos valeurs comparent la façon dont vous le souhaitez pourrait être la façon la plus simple de le faire.
sweenish
4
@ acraig5075, dans l'ordre décroissant, il devrait l'être return first > second.
ks1322
1
@ acraig5075 J'ai l'impression de manquer quelque chose, ou les gens ne connaissent-ils pas la différence entre ascendant et descendant ?
sweenish
3
Peut-être que vous cherchez std :: rotation ?
super

Réponses:

8

Votre fonction de comparaison est incorrecte car les valeurs que vous obtenez en tant que firstet secondsont les éléments du std::vector. Il n'est donc pas nécessaire de les utiliser comme indices. Donc, vous devez changer

return indices[first] > indices[second];

à

return first > second;

Maintenant, concernant le problème que vous essayez de résoudre ...

Vous pouvez laisser 3, 4, 5 et 6 hors de comparaison avec d'autres éléments et toujours les comparer les uns aux autres:

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

Démo

Casse Noisette
la source
@NutCracker Oui, je suis d'accord que c'est mieux d'avoir le premier critère en premier.
Débordement de tas le
5

Les fonctions de la bibliothèque d'algorithmes standards comme iota, sort, find, rotateet copyrendrait la vie plus facile. Votre exemple se résume à:

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

Production:

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


@TedLyngmo dans les commentaires montre bien qu'il pourrait / devrait être amélioré avec:

auto a = std::lower_bound(indices.begin(), indices.end(), 6, std::greater<int>{});
auto b = a + 4;
acraig5075
la source
auto b = a + 4;est incorrect (si vous souhaitez conserver la cohérence avec l'extrait de code précédent). Cela devrait être auto b = a + 3;dû au fait que std::rotatevous l'utilisezb + 1
Biagio Festa
3

Solution 1

Approche simple avec un comparateur non linéaire .

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

Solution 2

Utilisation de std::algorithms (partition)!

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

Considérations sur les performances

Il peut sembler que la deuxième solution est plus lente en raison de la surcharge de la partition. Ce n'est probablement pas le cas, en raison de la prédiction du cache et des branches manquantes dans les processeurs modernes.

Référence

Biagio Festa
la source
Tout bon compilateur devrait se transformer n <= 6 && 3 <= n en ce qui fonctionne le mieux pour le CPU cible afin que vous ne gagniez rien en introduisant les nombres 2 et 7 mais une confusion potentielle - et pourquoi prendre un pointeur sur le vecteur au lieu d'une référence?
Ted Lyngmo
N'utilisez pas `const int number` comme fonction d'argument
Antoine Morrier
1
@AntoineMorrier Pourquoi?
Débordement de tas le
@HeapOverflow Parce que c'est la même chose sans utiliser const :).
Antoine Morrier
@AntoineMorrier Je ne pense pas que ce soit la même chose. Le constlecteur ne dit- il pas que la fonction ne change pas la valeur? Dans ce cas particulier d'un monoplace, cela peut être clair, mais ce n'est généralement pas le cas.
Débordement de tas le