Guía Completa de Algoritmos de la STL en C++

1. Algoritmos de inspección (no modificadores)

Estos procedimientos analizan el contanido de un contenedor sin alterar los elementos existentes.

1.1 Búsqueda con find y find_if

  • find: Localiza la primera coincidencia de un valor específico.
  • find_if: Busca el primer elemento que cumpla con una condición lógica (predicado).
#include <vector>
#include <algorithm>
#include <iostream>

std::vector<int> valores = {10, 20, 30, 40, 50};

// Localizar un valor exacto
auto it = std::find(valores.begin(), valores.end(), 30);
if (it != valores.end()) {
    std::cout << "Encontrado: " << *it << std::endl;
}

// Buscar el primer múltiplo de 7
auto it_cond = std::find_if(valores.begin(), valores.end(), [](int n) {
    return n % 7 == 0; 
}); 
// it_cond será valores.end() si no hay coincidencias

1.2 Conteo con count y count_if

Permiten obtener la frecuencia de aparición de elementos según criterios definidos.

std::vector<int> datos = {1, 2, 2, 3, 4, 2, 5};
int total_doses = std::count(datos.begin(), datos.end(), 2); // Resultado: 3

int mayores_que_tres = std::count_if(datos.begin(), datos.end(), [](int x) {
    return x > 3;
}); // Resultado: 2

1.3 Verificación de predicados: all_of, any_of, none_of

Útiles para validaciones rápidas sobre rangos de datos.

std::vector<int> lista = {2, 4, 6, 8};

bool todos_pares = std::all_of(lista.begin(), lista.end(), [](int n) { return n % 2 == 0; });
bool algun_negativo = std::any_of(lista.begin(), lista.end(), [](int n) { return n < 0; });
bool ningun_cero = std::none_of(lista.begin(), lista.end(), [](int n) { return n == 0; });

2. Algoritmos de modificación de secuencias

Estas funciones alteran el orden, el valor o la estructura de los datos en el contenedor.

2.1 Transformación de datos

transform aplica una operación a cada elemento y almacena el resultado en un destino.

std::vector<int> entrada = {1, 2, 3, 4};
std::vector<int> salida;

// Calcular el cubo de cada número
std::transform(entrada.begin(), entrada.end(), std::back_inserter(salida), [](int n) {
    return n * n * n;
}); 

2.2 El modismo Erase-Remove

En C++, remove no elimina físicamente los elementos del contenedor (no cambia el tamaño), solo los desplaaz. Es necesario usar el método erase para limpiar el contenedor.

std::vector<int> nums = {10, 20, 30, 20, 40, 20};

// Mover los '20' al final y obtener el nuevo iterador final
auto nuevo_final = std::remove(nums.begin(), nums.end(), 20);

// Eliminar físicamente los elementos sobrantes
nums.erase(nuevo_final, nums.end()); 
// nums ahora es {10, 30, 40}

2.3 Reemplazo de valores

Sustituye elementos que coincidan con un valor o condición por uno nuevo.

std::vector<int> serie = {1, 5, 8, 5, 2};
std::replace(serie.begin(), serie.end(), 5, 99); 
// serie: {1, 99, 8, 99, 2}

3. Ordenamiento y búsqueda binaria

Para utilizar algoritmos de búsqueda eficiente, la secuencia debe estar previamente ordenada.

3.1 Tipos de ordenamiento

  • sort: Ordenamiento rápido (complejidad promedio O(N log N)).
  • stable_sort: Mantiene el orden relativo de elementos equivalentes.
  • partial_sort: Ordena solo los primeros N elementos.
std::vector<int> desorden = {9, 1, 5, 3, 7};
std::sort(desorden.begin(), desorden.end()); // {1, 3, 5, 7, 9}

3.2 Búsqueda en rangos ordenados

std::vector<int> ordenados = {10, 20, 20, 30, 40};

// lower_bound: primer elemento >= 20
auto lb = std::lower_bound(ordenados.begin(), ordenados.end(), 20); 

// upper_bound: primer elemento > 20
auto ub = std::upper_bound(ordenados.begin(), ordenados.end(), 20); 

bool existe = std::binary_search(ordenados.begin(), ordenados.end(), 30);

4. Algoritmos numéricos

Ubicados en la cabecera <numeric>, facilitan operaciones matemáticas sobre colecciones.

4.1 Acumulación e iota

#include <numeric>

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

// Sumar todos los elementos comenzando desde 0
int suma = std::accumulate(v.begin(), v.end(), 0); 

4.2 Diferencias adyacentes

std::vector<int> sec = {1, 2, 4, 7, 11};
std::vector<int> difs(sec.size());

std::adjacent_difference(sec.begin(), sec.end(), difs.begin());
// difs: {1, 1, 2, 3, 4}

5. Gestión de estructuras de Montículo (Heap)

Permite tratar un contenedor como un árbol binario donde el elemento máximo siempre está en la raíz.

std::vector<int> heap_data = {10, 50, 20, 40, 30};

std::make_heap(heap_data.begin(), heap_data.end());
// El máximo (50) está ahora en heap_data[0]

heap_data.push_back(60);
std::push_heap(heap_data.begin(), heap_data.end()); // Reordena para incluir 60

std::pop_heap(heap_data.begin(), heap_data.end()); // Mueve el máximo al final
heap_data.pop_back(); // Elimina el antiguo máximo

Preguntas Frecuentes

  • ¿Cuándo usar stable_sort sobre sort? Use stable_sort cuando el orden original de elementos con claves idénticas deba preservarse, por ejemplo, al ordenar una lista de usuarios por apellido que ya estaba ordenada por nombre.
  • ¿Por qué remove no borra los elementos? Los algoritmos de la STL operan sobre iteradores y no tienen conocimiento directo de la estructura interna del contenedor (como su capacidad o tamaño). Por ello, solo pueden mover valores; la gestión de memoria queda a cargo del contenedor mediante erase.
  • ¿Qué algoritmos requieren datos ordenados? Principalmente binary_search, lower_bound, upper_bound, equal_range, merge, includes y las operaciones de conjuntos (set_union, set_intersection, etc.).

Etiquetas: cpp STL algorithms Refactoring cpp-standard-library

Publicado el 6-30 18:32