Estos algoritmos no alteran los elementos de los contenedores con los que operan.
1.1 find y find_if
find(inicio, fin, valor): Devuelve un iterador al primer elemento que es igual alvalorespecificado.find_if(inicio, fin, predicado): Devuelve un iterador al primer elemento que satisface el predicado.
std::vector<double> lecturas_sensor = {10.5, 12.1, 8.7, 15.3, 9.2};
// Buscar la lectura de 12.1
auto it_sensor = std::find(lecturas_sensor.begin(), lecturas_sensor.end(), 12.1);
if (it_sensor != lecturas_sensor.end()) {
std::cout << "Lectura encontrada: " << *it_sensor << std::endl;
}
// Buscar primera lectura superior a 14.0
auto it_alerta = std::find_if(lecturas_sensor.begin(), lecturas_sensor.end(), [](double valor) {
return valor > 14.0;
});
1.2 count y count_if
count(inicio, fin, valor): Cuenta los elementos que son iguales alvalor.count_if(inicio, fin, predicado): Cuenta los elementos que cumplen el predicado.
std::vector<int> puntos_deteccion = {1, 0, 0, 1, 1, 0, 1};
int detecciones_positivas = std::count(puntos_deteccion.begin(), puntos_deteccion.end(), 1);
int umbrales_superados = std::count_if(puntos_deteccion.begin(), puntos_deteccion.end(), [](int val) {
return val > 0;
});
1.3 for_each
Aplica una función a cada elemento dentro de un rango.
std::vector<double> distancias = {1.2, 0.8, 2.5, 3.1};
std::for_each(distancias.begin(), distancias.end(), [](double& d) {
d *= 100.0; // Convertir metros a centímetros
});
1.4 equal y mismatch
equal(inicio1, fin1, inicio2): Comprueba si dos rangos son iguales.mismatch(inicio1, fin1, inicio2): Devuelve un par de iteradores que apuntan al primer elemento diferente entre dos rangos.
std::vector<int> ruta_A = {1, 2, 3};
std::vector<int> ruta_B = {1, 2, 4};
bool rutas_coinciden = std::equal(ruta_A.begin(), ruta_A.end(), ruta_B.begin());
auto diferencia = std::mismatch(ruta_A.begin(), ruta_A.end(), ruta_B.begin());
1.5 all_of, any_of, none_of
Evalúan condiciones lógicas sobre todos los elementos de un rango.
std::vector<bool> sensores_activos = {true, true, false};
bool sistema_completo = std::all_of(sensores_activos.begin(), sensores_activos.end(), [](bool s){ return s; });
bool hay_sensor = std::any_of(sensores_activos.begin(), sensores_activos.end(), [](bool s){ return s; });
2. Algoritmos de secuencia modificadora
Estos algoritmos pueden cambiar los elementos dentro del contenedor.
2.1 copy y copy_if
copy(inicio, fin, destino): Copia los elementos del rango fuente al destino.copy_if(inicio, fin, destino, predicado): Solo copia los elementos que cumplen el predicado.
std::vector<float> datos_gps_completos = {45.1, 45.2, 44.9, 45.3, 44.8};
std::vector<float> datos_validos;
std::copy_if(datos_gps_completos.begin(), datos_gps_completos.end(), std::back_inserter(datos_validos), [](float coord) {
return (coord > 44.95) && (coord < 45.25);
});
2.2 transform
Aplica una operación a los elementos de un rango y almacena el resultado en otro.
std::vector<int> ids_objetos = {101, 102, 103};
std::vector<std::string> etiquetas;
std::transform(ids_objetos.begin(), ids_objetos.end(), std::back_inserter(etiquetas), [](int id) {
return "OBJ_" + std::to_string(id);
});
2.3 replace, replace_if y replace_copy
Reemplazan valores específicos o que cumplen una condición, ya sea directamente o copiando a un nuevo contenedor.
std::vector<char> marca_temporal = {'A', 'B', 'A', 'C', 'A'};
// Reemplazar todas las 'A' por 'T' (Timestamp)
std::replace(marca_temporal.begin(), marca_temporal.end(), 'A', 'T');
2.4 remove, remove_if y erase
remove no elimina físicamente los elementos; los mueve al final del contenedor. Se debe usar junto con erase para reducir el tamaño.
std::vector<double> puntos_camino = {1.0, 0.0, 2.5, 0.0, 3.7};
auto nuevo_fin = std::remove(puntos_camino.begin(), puntos_camino.end(), 0.0);
puntos_camino.erase(nuevo_fin, puntos_camino.end()); // Elimina los ceros
2.5 unique
Elimina elementos consecutivos duplicados. Se suele usar después de ordenar.
std::vector<int> etiquetas_clase = {1, 2, 2, 3, 3, 3, 4};
auto final_unico = std::unique(etiquetas_clase.begin(), etiquetas_clase.end());
etiquetas_clase.erase(final_unico, etiquetas_clase.end());
2.6 reverse
Invierte el orden de los elementos en un rango.
std::vector<int> historial_errores = {1001, 1002, 1003};
std::reverse(historial_errores.begin(), historial_errores.end());
2.7 rotate
Realiza una rotación en el rango, moviendo un segmento al principio.
std::vector<std::string> secuencia_planificada = {"giro_izq", "avanzar", "detener", "giro_der"};
// Rotar para empezar por 'avanzar'
std::rotate(secuencia_planificada.begin(), secuencia_planificada.begin() + 1, secuencia_planificada.end());
3. Algoritmos de ordenación y relacionados
3.1 sort, stable_sort y partial_sort
sort: Ordenación rápida (introsort), no estable.stable_sort: Ordenación estable (mantiene el orden relativo de elementos iguales).partial_sort: Ordena solo una parte del rango.
struct ObjetoDetectado {
float distancia;
int id;
};
std::vector<ObjetoDetectado> objetos_cercanos = {{5.2, 1}, {3.1, 2}, {5.2, 3}, {2.8, 4}};
// Ordenar por distancia (estable para objetos a misma distancia)
std::stable_sort(objetos_cercanos.begin(), objetos_cercanos.end(), [](const ObjetoDetectado& a, const ObjetoDetectado& b) {
return a.distancia < b.distancia;
});
3.2 nth_element
Reorganiza el rango para que el elemento en la n-ésima posición sea el que estaría allí en una secuencia completamente ordenada.
std::vector<float> tiempos_respuesta = {0.1, 0.5, 0.3, 0.2, 0.4};
// Encontrar la mediana (elemento en la posición 2)
std::nth_element(tiempos_respuesta.begin(), tiempos_respuesta.begin() + 2, tiempos_respuesta.end());
float mediana = tiempos_respuesta[2];
3.3 binary_search, lower_bound, upper_bound
Requieren un rango ordenado. Se usan para búsquedas eficientes O(log n).
std::vector<int> ids_canales_ordenados = {10, 20, 30, 40, 50};
bool canal_30_activo = std::binary_search(ids_canales_ordenados.begin(), ids_canales_ordenados.end(), 30);
auto iter_sup = std::upper_bound(ids_canales_ordenados.begin(), ids_canales_ordenados.end(), 25); // Apunta a 30
3.4 merge
Fusiona dos rangos ordenados en un solo rango ordenado.
std::vector<int> puntos_ruta_1 = {1, 3, 5};
std::vector<int> puntos_ruta_2 = {2, 4, 6};
std::vector<int> ruta_fusionada(puntos_ruta_1.size() + puntos_ruta_2.size());
std::merge(puntos_ruta_1.begin(), puntos_ruta_1.end(), puntos_ruta_2.begin(), puntos_ruta_2.end(), ruta_fusionada.begin());
4. Algoritmos de montículo (heap)
Permiten tratar un rango como un montículo (heap), útil para gestionar prioridades.
std::vector<int> prioridades_tareas = {3, 1, 4, 1, 5, 9};
std::make_heap(prioridades_tareas.begin(), prioridades_tareas.end()); // Convierte en un max-heap
int tarea_mas_prioritaria = prioridades_tareas.front();
std::pop_heap(prioridades_tareas.begin(), prioridades_tareas.end()); // Mueve el mayor al final
prioridades_tareas.pop_back(); // Elimina la tarea más prioritaria
5. Algoritmos de valor mínimo/máximo
5.1 min_element y max_element
Devuelven iteradores al elemento mínimo y máximo en un rango.
std::vector<double> intensidades_senal = {0.8, 0.3, 0.9, 0.1, 0.5};
auto senal_mas_fuerte = std::max_element(intensidades_senal.begin(), intensidades_senal.end());
auto senal_mas_debil = std::min_element(intensidades_senal.begin(), intensidades_senal.end());
6. Algoritmos numéricos (en <numeric>)
6.1 accumulate
Calcula la suma acumulada (u otra operación) de los elemantos.
#include <numeric>
std::vector<float> desplazamientos = {1.0f, -0.5f, 0.3f};
float desplazamiento_neto = std::accumulate(desplazamientos.begin(), desplazamientos.end(), 0.0f);
6.2 iota
Rellena un rango con valores consecutivos incrementando a partir de un valor inicial.
std::vector<int> ids_secuenciales(10);
std::iota(ids_secuenciales.begin(), ids_secuenciales.end(), 1000); // IDs desde 1000
6.3 partial_sum
Calcula la suma parcial (acumulada) y la almacena en otro rango.
std::vector<float> velocidades_por_segundo = {10.0f, 12.0f, 11.0f, 13.0f};
std::vector<float> distancia_acumulada(velocidades_por_segundo.size());
std::partial_sum(velocidades_por_segundo.begin(), velocidades_por_segundo.end(), distancia_acumulada.begin());
7. Algoritmos de conjunto
Operan en rangos ordendaos para realizar operaciones teóricas de conjuntos.
std::vector<std::string> caracteristicas_lidar = {"punto", "nube", "reflejo"};
std::vector<std::string> caracteristicas_camara = {"nube", "pixel", "color"};
std::vector<std::string> caracteristicas_comunes;
std::set_intersection(caracteristicas_lidar.begin(), caracteristicas_lidar.end(),
caracteristicas_camara.begin(), caracteristicas_camara.end(),
std::back_inserter(caracteristicas_comunes));
// caracteristicas_comunes contendrá "nube"
8. Preguntas frecuentes
¿Cuándo usar stable_sort en lugar de sort?
Se prefiere stable_sort cuando se necesita mantener el orden relativo de elementos que se consideran iguales según el criterio de ordenación. Por ejemplo, al ordenar una lista de detecciones por timestamp y luego por prioridad, un stable_sort secundario por prioridad no desordenará las detecciones con la misma prioridad que ya estaban ordenadas por tiempo.
¿Por qué la combinación erase + remove es un idiom?
El algoritmo remove opera por sobrescritura, moviendo los elementos no eliminados al inicio y devolviendo un iterador al nuevo final lógico. Sin embargo, el tamaño del contenedor no cambia. La llamada a erase con ese nuevo final y el final real del contenedor es la que ajusta físicamente el tamaño y libera los recursos de los elementos "lógicamente" eliminados.
¿Qué algoritmos requieren contenedores ordenados?
La familia de búsqueda binaria (binary_search, lower_bound, upper_bound), los algoritmos de conjuntos (set_union, set_intersection, etc.) y merge dependen de que los rangos de entrada estén ordenados para funcionar correctamente y con eficiencia O(n log n) o mejor.