Ordenar un vector en orden descendente dentro de dos rangos

14

Digamos que tengo un vector de enteros:

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

Luego lo ordeno en orden descendente:

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

Esto produce lo siguiente:

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

Ahora quiero que los números 3, 4, 5 y 6 se muevan hasta el final, y mantener el orden descendente para ellos (preferiblemente sin tener que usarlos sortpor segunda vez). Es decir, esto es lo que quiero:

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

¿Cómo debo modificar la función de comparación de std::sortpara lograr eso?

Yury
fuente
44
return indices[first] > indices[second]No quiere decir return first < second;?
acraig5075
2
Para un tipo descendente simple, std::greaterdesde <functional>se puede usar en lugar de su lambda. En cuanto a su pregunta, escribir un comparador más detallado que garantice que sus valores se comparen de la manera deseada puede ser la forma más fácil de hacerlo.
Sweenish
44
@ acraig5075, en orden descendente debería ser return first > second.
ks1322
1
@ acraig5075 Siento que me falta algo, ¿o la gente no sabe la diferencia entre ascender y descender ?
sweenish
3
¿Quizás estás buscando std :: rotate ?
súper

Respuestas:

8

Su función de comparación es incorrecta ya que los valores que obtiene como firsty secondson los elementos de std::vector. Por lo tanto, no hay necesidad de usarlos como índices. Entonces, necesitas cambiar

return indices[first] > indices[second];

a

return first > second;

Ahora, con respecto al problema que intentas resolver ...

Puede dejar 3, 4, 5 y 6 fuera de comparación con otros elementos y aún compararlo entre sí:

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

Manifestación

Cascanueces
fuente
@NutCracker Sí, estoy de acuerdo en que es mejor tener primero el criterio principal.
Desbordamiento del montón
5

Funciones de la biblioteca de algoritmos estándar como iota, sort, find, rotatey copyharían la vida más fácil. Su ejemplo se reduce 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;
}

Salida:

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


@TedLyngmo en los comentarios señala que podría / debería mejorarse con:

auto a = std::lower_bound(indices.begin(), indices.end(), 6, std::greater<int>{});
auto b = a + 4;
acraig5075
fuente
auto b = a + 4;está mal (si desea mantener la coherencia con el fragmento anterior). Debería ser auto b = a + 3;porque en el std::rotateusob + 1
Biagio Festa
3

Solución 1

Enfoque directo con un comparador no lineal .

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

Solución 2

Usando std::algorithms (partición)!

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

Consideraciones de rendimiento

Puede parecer que la segunda solución es más lenta debido a la sobrecarga de la partición. Probablemente no lo sea, debido a la predicción de caché y rama errónea en los procesadores modernos.

Punto de referencia

Biagio Festa
fuente
Cualquier buen compilador debería transformarse n <= 6 && 3 <= n en lo que funcione mejor para la CPU de destino, de modo que no gane nada introduciendo los números 2 y 7 sino una posible confusión, y ¿por qué tomar un puntero al vector en lugar de una referencia?
Ted Lyngmo
No use `const int number` como función de argumento
Antoine Morrier
1
@AntoineMorrier ¿Por qué?
Desbordamiento del montón
@HeapOverflow Porque es lo mismo sin usar const :).
Antoine Morrier
@AntoineMorrier No creo que sea lo mismo. ¿No le constdice al lector que la función no cambia el valor? En este caso particular de una línea, puede ser claro, pero en general no lo es.
Desbordamiento del montón