Aplicaciones avanzadas del patrón Strategy en C++

Estos algoritmos no alteran los elementos del contenedor sobre el que operan.

1.1 find y find_if

  • find(inicio, fin, valor) – localiza la primera ocurrencia de valor y retorna un iterador (o fin si no existe).
  • find_if(inicio, fin, predicado) – localiza el primer elemento que satisface el predicado.
  • find_end(inicio, fin, sub_inicio, sub_fin) – localiza la última aparición de una subsecuencia.
std::vector<int> nums = {1, 3, 5, 7, 9};

// Buscar el valor 5
auto it = std::find(nums.begin(), nums.end(), 5);
if (it != nums.end())
    std::cout << "Encontrado: " << *it << "\n";  // 5

// Primer mayor que 6
auto it2 = std::find_if(nums.begin(), nums.end(), [](int x) { return x > 6; });
std::cout << "Primero >6: " << *it2 << "\n";  // 7

// Subsecuencia {3,5}
std::vector<int> sub = {3, 5};
auto it3 = std::find_end(nums.begin(), nums.end(), sub.begin(), sub.end());
if (it3 != nums.end())
    std::cout << "Subsecuencia comienza en índice " << (it3 - nums.begin()) << "\n";  // 1

1.2 count y count_if

  • count(inicio, fin, valor) – cuenta cuántas veces aparece valor.
  • count_if(inicio, fin, predicado) – cuanta elementos que cumplen predicado.
std::vector<int> datos = {1, 2, 3, 2, 4, 2};
int cuantos2 = std::count(datos.begin(), datos.end(), 2);          // 3
int pares = std::count_if(datos.begin(), datos.end(), [](int x) { 
    return x % 2 == 0; 
});                                                                 // 4

1.3 for_each

Aplica una función a cada elemento del rango.

std::vector<int> vals = {1, 2, 3, 4, 5};
std::for_each(vals.begin(), vals.end(), [](int& x) { x *= 2; });
// vals ahora {2, 4, 6, 8, 10}

1.4 equal y mismatch

  • equal(b1, e1, b2) – determina si dos rangos son iguales.
  • mismatch(b1, e1, b2) – retorna un par de iteradores al primer elemento diferente.
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {1, 2, 4};
std::vector<int> c = {1, 2, 3, 4};

bool eq = std::equal(a.begin(), a.end(), b.begin());
std::cout << "a == b? " << std::boolalpha << eq << "\n";  // false

auto par = std::mismatch(a.begin(), a.end(), c.begin());
if (par.first != a.end())
    std::cout << "Diferencia: " << *par.first << " vs " << *par.second << "\n";
// sin salida porque a y c son iguales en los primeros 3

1.5 all_of, any_of, none_of

std::vector<int> pares_vec = {2, 4, 6, 8};
bool todos_pares = std::all_of(pares_vec.begin(), pares_vec.end(), 
                               [](int x) { return x % 2 == 0; }); // true
bool algun_impar = std::any_of(pares_vec.begin(), pares_vec.end(), 
                               [](int x) { return x % 2 != 0; }); // false
bool ningun_negativo = std::none_of(pares_vec.begin(), pares_vec.end(), 
                                    [](int x) { return x < 0; }); // true

2. Algoritmos de modificación

2.1 copy y copy_if

std::vector<int> origen = {1, 2, 3, 4, 5};
std::vector<int> destino(5);
std::copy(origen.begin(), origen.end(), destino.begin());  // destino: [1,2,3,4,5]

std::vector<int> solo_pares;
std::copy_if(origen.begin(), origen.end(), std::back_inserter(solo_pares),
             [](int x) { return x % 2 == 0; });            // solo_pares: [2,4]

Nota: back_inserter invoca push_back automáticamente, no requiere espacio previo.

2.2 transform

std::vector<int> nums = {1, 2, 3};
std::vector<int> cuadrados(3);
std::transform(nums.begin(), nums.end(), cuadrados.begin(), 
               [](int x) { return x * x; });  // cuadrados: [1,4,9]

std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = {4, 5, 6};
std::vector<int> suma(3);
std::transform(v1.begin(), v1.end(), v2.begin(), suma.begin(),
               [](int a, int b) { return a + b; });  // suma: [5,7,9]

2.3 replace, replace_if y replace_copy

std::vector<int> vec = {1, 2, 3, 2, 5};
std::replace(vec.begin(), vec.end(), 2, 20);  // vec: [1,20,3,20,5]

std::replace_if(vec.begin(), vec.end(), [](int x){ return x > 10; }, 0);
// vec: [1,0,3,0,5]

std::vector<int> copia;
std::replace_copy(vec.begin(), vec.end(), std::back_inserter(copia), 3, 300);
// copia: [1,0,300,0,5]  (vec original sin cambios)

2.4 remove, remove_if y erase

std::vector<int> n = {1, 2, 3, 2, 4};
auto nuevo_fin = std::remove(n.begin(), n.end(), 2); // mueve los 2 al final
n.erase(nuevo_fin, n.end());                          // n: [1,3,4]

// Eliminar pares con remove_if
n = {1, 2, 3, 4, 5};
n.erase(std::remove_if(n.begin(), n.end(), [](int x){ return x % 2 == 0; }), 
        n.end());  // n: [1,3,5]

2.5 unique

std::vector<int> v = {1, 1, 2, 2, 3, 3, 3, 4, 5};
auto ult = std::unique(v.begin(), v.end());
v.erase(ult, v.end());  // v: [1,2,3,4,5]

2.6 reverse

std::vector<int> v = {1, 2, 3, 4, 5};
std::reverse(v.begin(), v.end());  // v: [5,4,3,2,1]

2.7 rotate

std::vector<int> v = {1, 2, 3, 4, 5};
std::rotate(v.begin(), v.begin() + 2, v.end());  // v: [3,4,5,1,2]

2.8 shuffle

#include <random>
std::vector<int> v = {1, 2, 3, 4, 5};
std::random_device rd;
std::mt19937 gen(rd());
std::shuffle(v.begin(), v.end(), gen);  // orden aleatorio

3. Ordenamiento

3.1 sort, stable_sort y partial_sort

std::vector<int> v = {5, 3, 1, 4, 2};
std::sort(v.begin(), v.end());                             // [1,2,3,4,5]
std::sort(v.begin(), v.end(), std::greater<int>());        // [5,4,3,2,1]

// stable_sort conserva orden relativo de elementos iguales
std::vector<std::pair<int,int>> vp = {{1,2},{2,1},{1,1}};
std::stable_sort(vp.begin(), vp.end(), 
                 [](auto& a, auto& b){ return a.first < b.first; });

// partial_sort: los 3 más pequeños ordenados al inicio
std::vector<int> v2 = {5, 3, 1, 4, 2, 6};
std::partial_sort(v2.begin(), v2.begin() + 3, v2.end());
// v2: primeros 3 elementos son 1,2,3; el resto desordenado

3.2 nth_element

std::vector<int> v = {5, 3, 1, 4, 2, 6};
std::nth_element(v.begin(), v.begin() + 2, v.end());
// v[2] es el 3er elemento más pequeño (3), izquierda <=3, derecha >=3

3.3 binary_search, lower_bound, upper_bound

Requieren rango ordenado.

std::vector<int> ord = {1, 3, 3, 5, 7};
bool existe = std::binary_search(ord.begin(), ord.end(), 3);   // true

auto lb = std::lower_bound(ord.begin(), ord.end(), 3);        // primer >=3, índice 1
auto ub = std::upper_bound(ord.begin(), ord.end(), 3);        // primer >3, índice 3

3.4 merge

std::vector<int> a = {1, 3, 5};
std::vector<int> b = {2, 4, 6};
std::vector<int> mezclado(a.size() + b.size());
std::merge(a.begin(), a.end(), b.begin(), b.end(), mezclado.begin());
// mezclado: [1,2,3,4,5,6]

4. Algoritmos de heap

std::vector<int> monticulo = {4, 1, 3, 2, 5};
std::make_heap(monticulo.begin(), monticulo.end());  // max‑heap: {5,4,3,2,1}

monticulo.push_back(6);
std::push_heap(monticulo.begin(), monticulo.end());  // reordena: {6,4,5,2,1,3}

std::pop_heap(monticulo.begin(), monticulo.end());   // mueve máximo al final
int maximo = monticulo.back();                       // 6
monticulo.pop_back();

std::sort_heap(monticulo.begin(), monticulo.end());  // orden ascendente: {1,2,3,4,5}

5. Algoritmos de mínimo/máximo

5.1 min y max

int minimo = std::min(5, 3);            // 3
int maximo = std::max(5, 3);            // 5
auto min_lista = std::min({4, 2, 8, 5}); // 2
auto max_lista = std::max({4, 2, 8, 5}); // 8

5.2 min_element y max_element

std::vector<int> v = {3, 1, 4, 2, 5};
auto min_it = std::min_element(v.begin(), v.end());  // apunta a 1
auto max_it = std::max_element(v.begin(), v.end());  // apunta a 5

5.3 minmax_element

auto par_minmax = std::minmax_element(v.begin(), v.end());
// par_minmax.first → 1, par_minmax.second → 5

6. Algoritmos numéricos (<numeric>)

6.1 accumulate

#include <numeric>
std::vector<int> v = {1, 2, 3, 4, 5};
int suma = std::accumulate(v.begin(), v.end(), 0);                 // 15
int prod = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>()); // 120

6.2 inner_product

std::vector<int> a = {1, 2, 3};
std::vector<int> b = {4, 5, 6};
int prod_punto = std::inner_product(a.begin(), a.end(), b.begin(), 0); // 32

6.3 iota

std::vector<int> v(5);
std::iota(v.begin(), v.end(), 10);  // v: [10,11,12,13,14]

6.4 partial_sum

std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dst(src.size());
std::partial_sum(src.begin(), src.end(), dst.begin());  // dst: [1,3,6,10,15]

6.5 adjacent_difference

std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dst(src.size());
std::adjacent_difference(src.begin(), src.end(), dst.begin());  // dst: [1,1,1,1,1]

7. Otros

7.1 generate

std::vector<int> v(5);
int n = 0;
std::generate(v.begin(), v.end(), [&n]() { return n++; });  // v: [0,1,2,3,4]

7.2 generate_n

std::vector<int> v(5);
int m = 10;
std::generate_n(v.begin(), 3, [&m]() { return m++; });  // primeros 3: [10,11,12]

7.3 includes

std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = {2, 4};
bool contiene = std::includes(v1.begin(), v1.end(), v2.begin(), v2.end()); // true

7.4 set_union, set_intersection, set_difference, set_symmetric_difference

std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = {3, 4, 5, 6, 7};
std::vector<int> res;

std::set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(res));
// res: [1,2,3,4,5,6,7]

res.clear();
std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(res));
// res: [3,4,5]

res.clear();
std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(res));
// res: [1,2]

res.clear();
std::set_symmetric_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(res));
// res: [1,2,6,7]

8. Preguntas frecuentes

  1. ¿Diferencia entre sort y stable_sort?
    sort usa introsort (inestable, O(n log n) promedio); stable_sort usa mergesort (estable, O(n log n) pero con mayor espacio).
  2. ¿Por qué remove neceista erase?
    remove solo mueve los elementos a conservar al inicio y devuelve un iterador al nuevo final lógico; no modifica el tamaño. erase lo elimina físicamente.
  3. ¿Qué algoritmos requieren rangos ordenados?
    Los de búsqueda binaria (binary_search, lower_bound, upper_bound), operaciones de conjuntos (set_intersection, set_union, etc.) y merge.

Etiquetas: C++ STL algoritmos ordenamiento búsqueda

Publicado el 7-13 00:32