Bibliotecas Estándar y Utilidades en C++. Una de los aspectos destacables de C++ radica en su Biblioteca Estándar (Standard Library), que incluye un conjunto muy completo de herramientas, algoritmos y estructuras de datos ya implementadas para hacer uso de ellas. Dentro de esta biblioteca destaca la STL (Standard Template Library), un conjunto de clases y funciones genéricas que permiten trabajar con colecciones de datos (contenedores), recorrerlas (iteradores), y transformarlas o consultarlas.
Este capítulo se centrará en hacer una introducción a las principales estructuras de datos y utilidades que ofrece STL, junto con otras herramientas como:
- Vectores y listas, como estructuras dinámicas para almacenar elementos.
- Conjuntos y mapas, para manejar datos únicos o pares clave-valor.
- Iteradores, para recorrer contenedores.
- Algoritmos estándar, como
sort
,find
,count
, entre muchos otros. - Funciones lambda, introducida en C++ Versión 11 que permite definir funciones anónimas.
El objetivo de este capítulo es brindarte una pequeña base en el uso de estas herramientas que te permitirán escribir código más agilmente.
13.1 STL (Standard Template Library)
La STL (Standard Template Library) es una parte fundamental de la biblioteca estándar de C++. Proporciona un conjunto de clases y funciones genéricas que permiten manipular colecciones de datos de manera eficiente, flexible y reutilizable.
Fue diseñada para ser altamente modular, aprovechando las capacidades de las plantillas (templates), lo que significa que puedes usar los mismos algoritmos y estructuras con diferentes tipos de datos.
Componentes principales de la STL
La STL se compone principalmente de tres grandes bloques:
Componente | Descripción | Ejemplos |
---|---|---|
Contenedores | Estructuras de datos que almacenan objetos. | vector , list , set , map , deque , etc. |
Algoritmos | Funciones genéricas para procesar los datos de los contenedores. | sort , find , count , reverse , etc. |
Iteradores | Objetos que permiten recorrer los elementos de un contenedor. | begin() , end() , ++ , * , etc. |
A estos se les puede sumar:
- Funciones lambda: funciones anónimas y cortas para ser usadas con algoritmos.
- Functores: objetos que actúan como funciones (menos usados hoy en día gracias a las lambdas).
- Adaptadores: transforman la interfaz de un contenedor o función (como
stack
,queue
,priority_queue
).
A continuación, veamos un pequeño ejemplo que usa un vector (contenedor), un algoritmo (sort
), e iteradores:
#include <iostream>
#include <vector>
#include <algorithm> // sort
int main() {
std::vector<int> numeros = {5, 2, 9, 1, 3};
std::sort(numeros.begin(), numeros.end()); // Ordena el vector de menor a mayor
std::cout << "Vector ordenado: ";
for (auto it = numeros.begin(); it != numeros.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
¿Qué tenemos aquí?
std::vector<int>
: crea un vector de enteros.std::sort
: algoritmo que ordena los elementos del vector.begin()
yend()
: devuelven iteradores al inicio y fin del contenedor.auto it
: deduce automáticamente el tipo del iterador.
13.2 Vectores (std::vector
)
¿Qué es un std::vector
?
std::vector
es una de las estructuras de datos más utilizadas de la STL. Es un contenedor secuencial que permite almacenar un conjunto de elementos del mismo tipo, con la capacidad de redimensionarse automáticamente cuando se añaden o eliminan elementos.
Podemos pensar en un vector
como un array dinámico que:
- puede crecer o reducir su tamaño durante la ejecución,
- gestiona automáticamente la memoria,
- proporciona funciones útiles para insertar, eliminar, acceder y recorrer elementos.
Sintaxis
#include <vector>
std::vector<Tipo> nombre_vector;
Ejemplo:
std::vector<int> numeros; // Vector vacío de enteros
std::vector<std::string> nombres(3); // Vector de 3 strings vacíos
std::vector<float> precios = {10.5, 3.99, 7.25}; // Inicialización con valores
Operaciones principales
- Agregar elementos
numeros.push_back(10); // Agrega un elemento al final
- Acceder a elementos
int x = numeros[0]; // Acceso sin verificación de rango
int y = numeros.at(1); // Acceso con verificación de rango (lanza excepción si se sale)
- Obtener tamaño
int tam = numeros.size();
- Eliminar el último elemento
numeros.pop_back();
- Vaciar el vector
numeros.clear();
- Recorrer con bucle tradicional
for (size_t i = 0; i < numeros.size(); i++) {
std::cout << numeros[i] << std::endl;
}
- Recorrer con bucle basado en rango (C++11)
for (int n : numeros) {
std::cout << n << std::endl;
}
- Con iteradores
for (auto it = numeros.begin(); it != numeros.end(); ++it) {
std::cout << *it << std::endl;
}
Veamos un ejemplo práctico
#include <iostream>
#include <vector>
int main() {
std::vector<int> edades;
edades.push_back(25);
edades.push_back(30);
edades.push_back(18);
std::cout << "Edades ingresadas: ";
for (int edad : edades) {
std::cout << edad << " ";
}
edades.pop_back(); // Elimina el último elemento
std::cout << "\nDespués de eliminar la última edad: ";
for (size_t i = 0; i < edades.size(); i++) {
std::cout << edades[i] << " ";
}
return 0;
}
Salida esperada:
Edades ingresadas: 25 30 18
Después de eliminar la última edad: 25 30
Ventajas de std::vector
frente a arrays tradicionales
std::vector | Array tradicional |
---|---|
Tamaño dinámico | Tamaño fijo |
Métodos útiles (push_back , size , etc.) | No posee métodos |
Mejor manejo de memoria | El programador debe gestionar memoria dinámica |
Compatible con algoritmos STL | Uso limitado con algoritmos |
Inicialización avanzada
std::vector<int> v1(5); // 5 elementos inicializados a 0
std::vector<int> v2(5, 100); // 5 elementos inicializados a 100
std::vector<int> v3 = {1, 2, 3}; // Lista de inicialización (C++11)
Tabla de operaciones con std::vector
Operación | Descripción | Ejemplo de uso |
---|---|---|
push_back(valor) | Añade un elemento al final del vector | v.push_back(10); |
pop_back() | Elimina el último elemento del vector | v.pop_back(); |
size() | Devuelve el número de elementos del vector | int n = v.size(); |
empty() | Devuelve true si el vector está vacío | if (v.empty()) { ... } |
clear() | Elimina todos los elementos del vector | v.clear(); |
at(pos) | Devuelve el elemento en la posición pos con verificación | int x = v.at(1); |
operator[] | Devuelve el elemento en la posición pos sin verificación | int x = v[1]; |
front() | Devuelve el primer elemento | int x = v.front(); |
back() | Devuelve el último elemento | int x = v.back(); |
insert(pos, valor) | Inserta valor en la posición pos (como iterador) | v.insert(v.begin() + 2, 99); |
erase(pos) | Elimina el elemento en la posición pos (como iterador) | v.erase(v.begin() + 1); |
begin() | Devuelve un iterador al inicio del vector | auto it = v.begin(); |
end() | Devuelve un iterador al final (uno después del último) | auto it = v.end(); |
assign(n, valor) | Asigna n elementos con el valor indicado | v.assign(5, 100); |
resize(n) | Cambia el tamaño del vector a n elementos | v.resize(10); |
swap(v2) | Intercambia los contenidos con otro vector v2 | v.swap(v2); |
capacity() | Muestra la capacidad interna del vector | size_t cap = v.capacity(); |
shrink_to_fit() | Reduce la capacidad para igualar el tamaño real | v.shrink_to_fit(); |
data() | Devuelve puntero al bloque de memoria del primer elemento | int* ptr = v.data(); |
13.3 Listas (std::list
)
¿Qué es std::list
?
std::list
es una estructura de datos de la Biblioteca Estándar de C++ que representa una lista doblemente enlazada. A diferencia de un std::vector
, que almacena sus elementos contiguamente en memoria, una lista enlazada está formada por nodos que contienen los datos y punteros a los nodos anterior y siguiente.
Esto implica que:
- La inserción y eliminación de elementos en cualquier parte de la lista es muy eficiente (constante, O(1)), porque solo implica cambiar punteros.
- El acceso aleatorio (acceder directamente a un elemento por índice) es lento (lineal, O(n)) porque hay que recorrer la lista desde el principio o el final.
- Es ideal para escenarios donde se hacen muchas inserciones y borrados en medio de la colección.
Declaración básica
#include <list>
std::list<int> miLista;
Para el manejo de listas, hay que tener unas consideraciones en cuenta:
- Los elementos se almacenan en nodos enlazados.
- No hay acceso por índice (
operator[]
no está definido). - Se puede recorrer con iteradores.
- Permite inserciones y eliminaciones eficientes en cualquier posición.
¿Qué operaciones se pueden realizar?
Para el manejo de listas nos apoyamos en un conjunto de funciones que nos facilitan la labor del manejo de los elementos de la lista. Veamoslos en una tabla.
Operación | Descripción | Ejemplo |
---|---|---|
push_back(valor) | Inserta al final | miLista.push_back(10); |
push_front(valor) | Inserta al principio | miLista.push_front(5); |
pop_back() | Elimina el último elemento | miLista.pop_back(); |
pop_front() | Elimina el primer elemento | miLista.pop_front(); |
insert(pos, valor) | Inserta antes de la posición indicada (iterador) | auto it = miLista.begin(); miLista.insert(it, 7); |
erase(pos) | Elimina el elemento en la posición indicada (iterador) | auto it = miLista.begin(); miLista.erase(it); |
size() | Devuelve el número de elementos | size_t n = miLista.size(); |
empty() | Comprueba si la lista está vacía | if (miLista.empty()) { ... } |
clear() | Elimina todos los elementos | miLista.clear(); |
begin() | Devuelve iterador al primer elemento | auto it = miLista.begin(); |
end() | Devuelve iterador al final (no apunta a un elemento válido) | auto it = miLista.end(); |
front() | Devuelve el primer elemento | int x = miLista.front(); |
back() | Devuelve el último elemento | int x = miLista.back(); |
remove(valor) | Elimina todos los elementos que coincidan con valor | miLista.remove(10); |
sort() | Ordena la lista (requiere elementos comparables) | miLista.sort(); |
reverse() | Invierte el orden de los elementos | miLista.reverse(); |
Ejemplo práctico de uso de std::list
#include <iostream>
#include <list>
int main() {
std::list<int> numeros;
// Insertar elementos
numeros.push_back(10);
numeros.push_front(5);
numeros.push_back(20);
// Recorrer la lista
std::cout << "Elementos en la lista: ";
for (int n : numeros) {
std::cout << n << " ";
}
std::cout << std::endl;
// Insertar en posición específica
auto it = numeros.begin();
++it; // Apunta al segundo elemento
numeros.insert(it, 15);
// Eliminar un elemento (el primero)
numeros.pop_front();
// Mostrar contenido tras cambios
std::cout << "Lista tras inserción y eliminación: ";
for (auto elem : numeros) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
Salida:
Elementos en la lista: 5 10 20
Lista tras inserción y eliminación: 15 10 20
Consideraciones
- Si necesitas acceso rápido por índice,
std::vector
es mejor. - Para inserciones/borrados frecuentes en medio de la colección,
std::list
es más eficiente. - El uso de iteradores es fundamental para trabajar con listas.
13.4 Conjuntos y Mapas (std::set
, std::map
)
Tanto los conjuntos (std::set
) como los mapas (std::map
) forman parte de la STL (Standard Template Library) de C++, y se basan internamente en estructuras de árboles binarios balanceados (como árboles rojo-negro). Esto les permite mantener los elementos ordenados y realizar búsquedas, inserciones y eliminaciones edeforma muy rápida, el tiempo de acceso en estas estructuras viene determinada por el tiempo logarítmico (O(log n)).
std::set
Un std::set
es una colección de elementos únicos ordenados automáticamente. No permite duplicados, y los elementos se almacenan siguiendo una ordenación natural (por defecto, de menor a mayor).
Declaración:
#include <set>
std::set<int> conjunto;
Operaciones comunes con std::set
El conjunto de operaciones que podemos realizar sobre los conjuntos te los muestro en la siguiente tabla:
Operación | Descripción | Ejemplo |
---|---|---|
insert(valor) | Inserta un nuevo elemento | conjunto.insert(5); |
erase(valor) | Elimina un elemento (si existe) | conjunto.erase(5); |
find(valor) | Devuelve iterador al elemento, o end() si no se encuentra | auto it = conjunto.find(5); |
count(valor) | Devuelve 1 si el valor existe, 0 si no | if (conjunto.count(5)) {...} |
begin() / end() | Iteradores para recorrer el conjunto | for (int x : conjunto) { ... } |
Veamos un ejemplo de como se usan:
#include <iostream>
#include <set>
int main() {
std::set<int> numeros = {3, 1, 4, 1, 2, 5};
numeros.insert(6);
numeros.erase(1);
std::cout << "Contenido del set: ";
for (int n : numeros)
std::cout << n << " ";
return 0;
}
Salida:
Contenido del set: 2 3 4 5 6
Algunos de los usos donde se hace indicado utilizar conjuntos podrían ser:
- Almacenar una colección de elementos únicos.
- Eliminar duplicados de un conjunto de datos.
- Mantener elementos ordenados automáticamente.
- Buscar rápidamente si un elemento existe en una colección.
- Resolver problemas de teoría de conjuntos (intersección, unión, diferencia).
- Implementar algoritmos de grafos (nodos visitados).
- Gestionar horarios o eventos sin duplicados.
- Filtrar datos en tiempo real con eliminación automática de duplicados.
std::map
Un std::map
es una colección de pares clave-valor. Las claves son únicas y se ordenan automáticamente. Cada clave se asocia a un único valor.
Declaración
#include <map>
std::map<std::string, int> edades;
Operaciones comunes con std::map
Operación | Descripción | Ejemplo |
---|---|---|
map[key] = valor | Inserta o modifica el valor asociado a la clave | edades["Ana"] = 30; |
insert({clave, valor}) | Inserta par clave-valor si la clave no existe | edades.insert({"Luis", 25}); |
erase(clave) | Elimina la clave y su valor asociado | edades.erase("Luis"); |
find(clave) | Devuelve iterador a la clave, o end() si no está | auto it = edades.find("Ana"); |
count(clave) | Retorna 1 si la clave existe, 0 si no | if (edades.count("Ana")) {...} |
begin() / end() | Iteradores para recorrer el mapa | for (auto p : edades) {...} |
Veamos un ejemplo:
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> edades;
edades["Juan"] = 25;
edades["María"] = 30;
edades["Carlos"] = 28;
for (auto par : edades) {
std::cout << par.first << ": " << par.second << std::endl;
}
return 0;
}
Salida:
Carlos: 28
Juan: 25
María: 30
Podemos utilizar std::map
en casos como:
- Asociar claves con valores (por ejemplo, nombre → edad).
- Implementar diccionarios o tablas de símbolos.
- Contar la frecuencia de elementos (por ejemplo, conteo de palabras).
- Almacenar configuraciones o parámetros con nombres descriptivos.
- Indexar datos con claves no numéricas (por ejemplo, strings).
- Crear índices invertidos (clave: palabra, valor: lista de posiciones).
- Resolver problemas de programación competitiva (par clave-valor).
- Representar grafos mediante listas de adyacencia (nodo → vecinos).
Diferencias clave entre std::set
y std::map
Característica | std::set | std::map |
---|---|---|
Tipo de datos | Solo valores | Pares clave-valor |
Claves | El valor es la clave | Las claves son independientes del valor |
Acceso por clave | No | Sí (map[clave] ) |
Duplicados | No | No (claves únicas) |
Orden automático | Sí | Sí |
13.5 iteradores en C++
Los iteradores son objetos que actúan como punteros que nos permiten recorrer los elementos de un contenedor de la libería STL (como vector
, list
, set
, map
, etc.) de una forma sencilla y segura. Proveen una manera estándar de acceder secuencialmente a los elementos sin conocer la implementación interna del contenedor.
Podemos pensar en ellos como «punteros inteligentes» que se usan para iterar sobre estructuras de datos.
Tipos de iteradores
C++ define varios tipos de iteradores, dependiendo del contenedor y de las operaciones permitidas:
Tipo de Iterador | Descripción |
---|---|
input_iterator | Sólo lectura hacia adelante. |
output_iterator | Sólo escritura hacia adelante. |
forward_iterator | Lectura/escritura hacia adelante. |
bidirectional_iterator | Lectura/escritura hacia adelante y atrás. |
random_access_iterator | Acceso directo a cualquier posición (como en vector ). |
Sintaxis básica de iteradores
Ejemplo con std::vector
:
#include <iostream>
#include <vector>
int main() {
std::vector<int> numeros = {10, 20, 30, 40};
// Usando un iterador
std::vector<int>::iterator it;
for (it = numeros.begin(); it != numeros.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
Salida:
10 20 30 40
@TIP *it
accede al valor apuntado por el iterador.
Iteradores constantes (const_iterator
)
Cuando no queremos modificar los elementos del contenedor:
std::vector<int>::const_iterator it;
for (it = numeros.cbegin(); it != numeros.cend(); ++it) {
std::cout << *it << std::endl;
}
Iteradores inversos (rbegin
, rend
)
Permiten recorrer los elementos en orden inverso:
for (std::vector<int>::reverse_iterator rit = numeros.rbegin(); rit != numeros.rend(); ++rit) {
std::cout << *rit << " ";
}
Salida:
40 30 20 10
Iteradores automáticos (auto
)
Desde C++11, se puede usar auto
para simplificar el código:
for (auto it = numeros.begin(); it != numeros.end(); ++it) {
std::cout << *it << " ";
}
Iteradores en otros contenedores
Con std::map
:
std::map<std::string, int> edades = {
{"Ana", 30},
{"Luis", 25},
{"Carlos", 40}
};
for (auto it = edades.begin(); it != edades.end(); ++it) {
std::cout << it->first << " tiene " << it->second << " años.\n";
}
13.6 Algoritmos útiles de la librería standar STL
La librería estandar STL (Standard Template Library) de C++ incluye una serie de algoritmos genéricos que nos facilitan la labor de desarrollo al traerlos ya incorporados. Estos algoritmos se encuentran en el encabezado <algorithm>
, y nos permiten realizar tareas como búsqueda, ordenación, copia, eliminación, transformación y más, sin necesidad de escribir el código desde cero.
Vamos a hacer un breve repaso sobre los más útiles y utilizados
std::sort
Ordena los elementos, por defecto en orden ascendente.
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numeros = {5, 2, 9, 1};
std::sort(numeros.begin(), numeros.end());
for (int n : numeros)
std::cout << n << " ";
}
Salida:
1 2 5 9
🔁También se puede usar un comparador personalizado:
std::sort(numeros.begin(), numeros.end(), std::greater<int>());
Esto los ordena en orden descendente.
std::find
Busca un valor en el contenedor y devuelve un iterador a él (o end()
si no se encuentra).
it = std::find(numeros.begin(), numeros.end(), 5);
if (it != numeros.end()) {
std::cout << "Encontrado: " << *it << "\n";
}
std::count
Cuenta cuántas veces aparece un valor específico en el contenedor.
int veces = std::count(numeros.begin(), numeros.end(), 5);
std::cout << "El número 5 aparece " << veces << " veces\n";
std::reverse
Invierte el orden de los elementos de un contenedor.
std::reverse(numeros.begin(), numeros.end());
std::for_each
Aplica una función o lambda a cada elemento de un contenedor.
std::for_each(numeros.begin(), numeros.end(), [](int n) {
std::cout << n * 2 << " ";
});
std::remove
ystd::erase
Elimina elementos de un contenedor. En vector
, se suele usar con erase
:
numeros.erase(std::remove(numeros.begin(), numeros.end(), 2), numeros.end());
@Tip. std::remove
reordena los elementos y devuelve un nuevo end
lógico, no elimina físicamente.
std::accumulate
(requiere<numeric>
)
Suma todos los elementos (u otra operación acumulativa).
#include <numeric>
int suma = std::accumulate(numeros.begin(), numeros.end(), 0);
std::sort
con estructuras personalizadas
struct Persona {
std::string nombre;
int edad;
};
std::vector<Persona> personas = {
{"Ana", 30},
{"Luis", 25},
{"Carlos", 35}
};
std::sort(personas.begin(), personas.end(), [](const Persona &a, const Persona &b) {
return a.edad < b.edad;
});
13.7. Funciones Lambda
Las funciones lambda son una característica introducida en C++11 que permite definir funciones anónimas (es decir, sin nombre) directamente en línea, generalmente para pasarlas como argumento a algoritmos o funciones.
Son especialmente útiles cuando queremos definir comportamientos cortos y específicos, por ejemplo, para ordenar, filtrar o aplicar una operación rápida sin necesidad de crear una función aparte.
Sintaxis:
[captura](parámetros) -> tipo_retorno {
// cuerpo
}
[captura]
: define qué variables externas puede usar la lambda.(parámetros)
: los argumentos de la función.-> tipo_retorno
(opcional): especifica el tipo de retorno.{ ... }
: cuerpo de la lambda.
En la mayoría de casos, el compilador puede deducir el tipo de retorno automáticamente, por lo que suele omitirse.
Ejemplo:
#include <iostream>
int main() {
auto cuadrado = [](int x) {
return x * x;
};
std::cout << "El cuadrado de 5 es: " << cuadrado(5) << "\n";
}
Usos con algoritmos de STL
Las lambdas se utilizan mucho con algoritmos como sort
, for_each
, find_if
, etc.
- Ordenar con lambda:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v = {5, 2, 9, 1};
std::sort(v.begin(), v.end(), [](int a, int b) {
return a > b; // Orden descendente
});
for (int n : v)
std::cout << n << " ";
}
for_each
con lambda:
std::for_each(v.begin(), v.end(), [](int n) {
std::cout << "Elemento: " << n << "\n";
});
- Captura de variables
Una lambda puede capturar variables externas (del ámbito en el que está definida) de varias formas:
Captura | Significado |
---|---|
[ ] | No captura nada |
[=] | Captura todas las variables por valor |
[&] | Captura todas las variables por referencia |
[x] | Captura x por valor |
[&x] | Captura x por referencia |
[=, &x] | Todo por valor, excepto x por referencia |
Veamos un ejemplo de captura por valor:
int factor = 2;
auto multiplicar = [factor](int x) {
return x * factor;
};
std::cout << multiplicar(3); // Imprime 6
Y ahora un ejemplo de captura por referencia:
int total = 0;
std::vector<int> v = {1, 2, 3};
std::for_each(v.begin(), v.end(), [&total](int n) {
total += n;
});
std::cout << "Suma total: " << total;
Ventajas de las lambdas
- Código más conciso y legible.
- Evita escribir funciones auxiliares innecesarias.
- Permite usar lógica en línea.
- Muy potentes con algoritmos STL.
Conclusión
En este capítulo exploramos la STL (Standard Template Library) de C++, que ofrece estructuras de datos y algoritmos listos para usar. Vimos:
std::vector
: arrays dinámicos que permiten acceso rápido y redimensionamiento automático.std::list
: listas doblemente enlazadas útiles para inserciones y eliminaciones frecuentes.std::set
ystd::map
: estructuras ordenadas para almacenar elementos únicos y pares clave-valor.- Iteradores: herramientas para recorrer contenedores de forma flexible y eficiente.
- Algoritmos STL: como
sort
,find
,count
, entre otros, para manipular contenedores. - Funciones lambda: funciones anónimas útiles para aplicar lógica directamente dentro de los algoritmos.
Vayamos a la parte prácica del capítulo.
TEjercicio 1: Ordenar una lista de números y eliminar duplicados
Enunciado:
Escribe un programa que reciba una lista de enteros, elimine los duplicados y los ordene de menor a mayor utilizando la STL.
Enunciado:
Escribe un programa que reciba una lista de enteros, elimine los duplicados y los ordene de menor a mayor utilizando la STL.
Código:
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
int main() {
std::vector<int> numeros = {5, 3, 8, 3, 9, 1, 5, 3, 8};
std::set<int> sin_duplicados(numeros.begin(), numeros.end()); // elimina duplicados
std::vector<int> resultado(sin_duplicados.begin(), sin_duplicados.end());
std::sort(resultado.begin(), resultado.end());
std::cout << "Números ordenados sin duplicados: ";
for (int n : resultado) {
std::cout << n << " ";
}
std::cout << std::endl;
return 0;
}
Explicación:
- Se usa un
std::set
para eliminar duplicados automáticamente (losset
no permiten valores repetidos). - Luego, se convierte de nuevo a
vector
para poder aplicarstd::sort
. - Finalmente, se imprime la lista ordenada.
Ejercicio 2: Contar ocurrencias de palabras en un texto
Enunciado:
Escribe un programa que cuente cuántas veces aparece cada palabra en un vector de strings. Usa std::map
.
Enunciado:
Escribe un programa que cuente cuántas veces aparece cada palabra en un vector de strings. Usa
std::map
.Código:
#include <iostream>
#include <map>
#include <vector>
#include <string>
int main() {
std::vector<std::string> palabras = {"casa", "sol", "casa", "luna", "sol", "casa"};
std::map<std::string, int> conteo;
for (const std::string& palabra : palabras) {
conteo[palabra]++;
}
std::cout << "Frecuencia de palabras:\n";
for (const auto& par : conteo) {
std::cout << par.first << ": " << par.second << "\n";
}
return 0;
}
Explicación:
Luego se imprimen todas las palabras y sus frecuencias.
Se usa un std::map
para asociar cada palabra con un contador.
Cada vez que una palabra aparece, se incrementa su valor en el mapa.
Ejercicio 3: Filtrar valores pares usando lambda y std::copy_if
Enunciado:
Usa una función lambda con std::copy_if
para filtrar los números pares de un std::vector
y guardarlos en otro vector.
std::copy_if
Enunciado:
Usa una función lambda con
std::copy_if
para filtrar los números pares de un std::vector
y guardarlos en otro vector.Código:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numeros = {1, 2, 3, 4, 5, 6, 7, 8};
std::vector<int> pares;
std::copy_if(numeros.begin(), numeros.end(), std::back_inserter(pares),
[](int n) { return n % 2 == 0; });
std::cout << "Números pares: ";
for (int n : pares) {
std::cout << n << " ";
}
std::cout << std::endl;
return 0;
}
Explicación:
El resultado son solo los números pares.
Se usa std::copy_if
con una lambda que devuelve true
si el número es par.
std::back_inserter
permite insertar elementos al final del vector de salida (pares
).
Bibliografía del tutorial de C/C++.
- C/C++. Curso de programación. Autor: Miguel Angel Acera (Editorial: Anaya Multimedia)
- C/C++. Curso de programación. Autor: Francisco José Ceballos (Editorial: RA-MA)
- Un recorrido por C++. Autor Bjarne Stroustrup (Editorial: Anaya Multimedia)
- 115 Ejercicios resueltos de programación C++. Autor Jorge Fernando Betancourt e Inma Yolanda Polanco (Editorial: RA-MA)