Curso exprés · No. 23
Una data structure no es más que una manera de organizar los datos para que las cosas que haces más a menudo sean rápidas. Elige la correcta y una operación es instantánea; elige la equivocada y la misma tarea se arrastra. Big-O es el lenguaje para esa diferencia — cómo crece el coste de una operación a medida que crecen los datos.
16 conceptos · un diagrama para cada uno · chuleta al final
Antes de las estructuras concretas, la idea detrás de todas ellas. Una data structure no es algo académico — es la única decisión que más a menudo determina si tu código es rápido o dolorosamente lento.
El mapa: 16 conceptos en seis grupos
Un mapa de metro: primero ves todas las líneas, luego viajas estación por estación.
Arriba hay tres ideas básicas: Data Structure, Trade-off y Big-O. Debajo, seis grupos: las formas de crecimiento de Big-O, el array, el hash map y el set, las estructuras con orden (stack y queue), las estructuras con forma (tree y graph) y, por último, cómo elegir entre ellas en la práctica.
Cómo leer este curso
Un pasaporte: los mismos campos para todos, así es fácil compararlos.
Cada concepto recibe el mismo tratamiento: su nombre en inglés (el término que encontrarás en la documentación y en las entrevistas), una imagen de la vida diaria, un diagrama y una ficha: qué es, cómo funciona, un ejemplo, el coste y cómo se relaciona con otras estructuras. La única regla de este curso: no existe la mejor estructura — solo la que encaja con la operación que más haces.
Data Structure — organizar los datos para las operaciones que más haces
Una caja de herramientas ordenada con ranuras etiquetadas frente a un montón de herramientas en un saco — las mismas herramientas, pero una te deja tomar la correcta al instante y la otra te obliga a hurgar cada vez.
Una data structure es una manera de disponer los datos en memoria. Distintas disposiciones hacen rápidas distintas operaciones: una es estupenda para buscar cosas por nombre, otra para mantener el orden, otra para tomar siempre el elemento más reciente. Todo el juego consiste en hacer coincidir la estructura con lo que harás más a menudo.
La misma tarea puede ser trivial o casi imposible según cómo se guarden los datos. Encontrar una palabra en un diccionario ordenado lleva segundos; encontrarla entre esas mismas palabras volcadas en orden aleatorio podría llevar todo el día. Así que una cantidad sorprendente de «haz esto rápido» es en realidad «guárdalo con la forma correcta». La estructura es la decisión.
- Qué es
Una manera de disponer los datos en memoria para operaciones concretas.
- Cómo funciona
La disposición decide de antemano qué operaciones son baratas y cuáles caras.
- Ejemplo
Buscar a un usuario por ID: instantáneo en una estructura, un suplicio por todo en otra.
- Coste
- Una mala elección no se arregla después con «optimización ingeniosa».
- Relaciones
Las estructuras se comparan con Big-O y se eligen mediante un Trade-off.
Trade-off — toda estructura renuncia a algo
Un auto deportivo y un camión de mudanzas son ambos vehículos — no preguntas cuál es mejor, preguntas mejor para qué.
Ninguna estructura es la mejor en todo. Una ofrece búsqueda instantánea pero ningún orden; otra mantiene el orden pero es lenta de buscar; otra es rápida para añadir pero lenta para encontrar dentro. Eso es el trade-off: la fortaleza en una operación se paga con debilidad en otra. No hay almuerzo gratis — tu trabajo es elegir la compensación que encaja con tu uso.
- Qué es
La fortaleza de una estructura en una operación siempre cuesta debilidad en otra.
- Ejemplo
Un Hash Map encuentra por clave al instante pero no guarda orden; un Array guarda el orden pero es lento al buscar por valor.
- Cuándo pensarlo
Cada vez que decides dónde viven los datos: ¿qué hago a menudo y qué rara vez?
- Relaciones
La compensación se mide en Big-O — cada operación tiene una «forma».
Una data structure organiza los datos para que tus operaciones comunes sean rápidas. La forma correcta vuelve fácil una tarea difícil — y toda estructura cambia velocidad en una operación por debilidad en otra.
Para comparar estructuras necesitas una manera de hablar de velocidad que no dependa de la máquina. Big-O es ese lenguaje: mide cómo crece el trabajo a medida que crecen los datos.
Big-O notation — crecimiento, no segundos
Preguntar no cuánto tarda una entrega, sino cómo cambia el tiempo total a medida que la ruta pasa de diez paradas a diez mil — la forma del crecimiento, no la lectura de un cronómetro.
La notación Big-O describe cómo crece el coste de una operación a medida que crece la cantidad de datos, llamada n. Ignora los milisegundos exactos y la computadora concreta, y captura la forma: ¿duplicar los datos duplica el trabajo, lo cuadruplica, o apenas lo cambia? Esa forma es lo que decide si algo se mantiene rápido a escala, así que Big-O es cómo los ingenieros comparan enfoques sin llegar a ejecutarlos.
- Qué es
Una descripción de cómo crece el trabajo al crecer el tamaño de datos n.
- Cómo funciona
Se descartan los segundos exactos y los detalles del hardware; se conserva la forma del crecimiento.
- Ejemplo
«Buscar en un array es O(n)»: diez veces más elementos — aproximadamente diez veces más trabajo.
- Ojo
- No dice cuántos segundos; dice cómo escala.
- Relaciones
- Las formas habituales son las Complexity classes.
Complexity classes — formas de excelente a pésima
Cuatro maneras en que una tarea escala: una se queda en un solo paso sin importar el montón, una crece con el montón, una explota como el montón al cuadrado, y una apenas crece aunque el montón lo haga.
Unas pocas formas cubren la mayoría del código:
- O(1) — constante: el mismo tiempo sin importar el tamaño (tomar un elemento por index).
- O(log n) — logarítmica: apenas crece aunque los datos se disparen (reducir la búsqueda a la mitad en cada paso).
- O(n) — lineal: crece al ritmo de los datos (recorrer una lista).
- O(n²) — cuadrática: explota (comparar cada elemento con todos los demás). La clásica «bien en pruebas, muere en producción».
Con diez elementos, cualquier estructura se siente instantánea — las diferencias no se notan. Big-O trata de lo que pasa cuando n se hace grande. Por eso las cosas se vuelven lentas «de repente» en producción: la mala forma siempre estuvo ahí, solo que oculta hasta que los datos crecieron.
- Qué es
- Nombres para las formas típicas de crecimiento.
- Ejemplo
Index de un array — O(1); búsqueda en un árbol equilibrado — O(log n); recorrer una lista — O(n); un bucle dentro de otro bucle — O(n²).
- Ojo
O(n²) va bien con datos de prueba y se atasca minutos con un millón de registros.
- Regla
O(1) y O(log n) sobreviven a la escala; O(n) es honesta; O(n²) es una alarma.
- Relaciones
Map before loop es la principal manera de convertir O(n²) en O(n).
Big-O mide cómo crece el trabajo con los datos, no los segundos. O(1) y O(log n) sobreviven a la escala; O(n) es honesta; O(n²) va bien en pruebas y muere en producción.
La estructura más básica es el array, o lista: una secuencia ordenada de elementos. Saber exactamente en qué es rápido y en qué es lento fija la base para todo lo demás.
Array — una secuencia ordenada y numerada
Una fila de casilleros numerados: los elementos están en orden y cada uno tiene una posición — casillero 0, casillero 1, casillero 2 — así que puedes ir directo a cualquiera por su número.
Un array (o list) guarda los elementos en orden, cada uno en una posición numerada llamada index, que empieza en 0. Como los elementos están en una disposición conocida y contigua, saltar a cualquier posición por su index es instantáneo — O(1). Los arrays son la opción por defecto para «un montón de cosas en orden», y el acceso directo por posición es su superpoder.
Hay un precio. Añadir o quitar al final es barato. Pero insertar o borrar en el medio desplaza todo lo que viene después para mantener intactos el orden y la numeración — O(n).
- Qué es
- Elementos en orden, cada uno con un index numerado desde cero.
- Rápido
- Acceso por index — O(1); añadir al final — barato.
- Lento
- Buscar por valor — O(n); insertar o borrar en el medio — O(n).
- Ejemplo
Un feed de publicaciones que se lee en orden y al que se añade al final.
- Cuándo usarlo
- Lees por posición y sobre todo añades al final.
- Relaciones
Su punto débil — buscar por valor — lo arregla un Hash Map; Stack y Queue suelen construirse sobre él.
Linear search — encontrar por valor
Puedes abrir el casillero 500 al instante — pero si solo sabes qué hay dentro y no en qué casillero, tienes que abrirlos uno por uno hasta encontrarlo.
La trampa: un array es rápido solo cuando conoces el index. Para encontrar un elemento por su contenido — «¿está este email en la lista?» — tienes que revisar cada uno por turno. Eso es la linear search, O(n): lenta en listas grandes. Ese hueco es justo el problema que resuelve la siguiente estructura.
- Qué es
- Revisar los elementos uno por uno hasta encontrar el que quieres.
- Coste
- O(n): diez veces más datos — diez veces más tiempo.
- Ejemplo
emails.includes(x)en una lista de un millón de direcciones.- Ojo
- Dentro de otro bucle se vuelve O(n²) — el clásico «lento en producción».
- Relaciones
- Se sustituye por una búsqueda O(1) en un Hash Map o Set.
Un array es una secuencia ordenada e indexada: acceso instantáneo por posición, pero O(n) para buscar por valor o insertar en el medio.
Si una estructura merece ser tu caballo de batalla por defecto, es el hash map. Resuelve la mayor debilidad del array — encontrar cosas por valor — y lo hace casi al instante.
Hash Map — pares clave-valor
Una guía telefónica: buscas a una persona por su nombre y obtienes su número al instante. No recorres todas las entradas — el nombre te lleva directo a la respuesta.
Un hash map (también llamado diccionario, hash table o simplemente map) guarda los datos como pares clave-valor: le das una clave y te devuelve el valor correspondiente. Un usuario por ID, un ajuste por nombre, un conteo por palabra — directamente, por la clave. Donde un array te obliga a conocer la posición, un hash map te deja encontrar por un nombre con sentido.
- Qué es
- Pares clave → valor con búsqueda por clave.
- Rápido
- Buscar, insertar y borrar por clave — en la práctica O(1).
- Lento
Sin orden; encontrar «por valor» en lugar de por clave vuelve a ser un recorrido.
- Ejemplo
users[id],settings["theme"], conteo de palabrascounts[word] += 1.- Cuándo usarlo
- Buscas constantemente por un identificador.
- Relaciones
Su velocidad viene de la Hash function; sin valores se convierte en un Set.
Hash function — por qué la búsqueda es instantánea
Un archivador mágico que, dada una etiqueta, abre directo el cajón correcto sin revisar los demás — el número de cajones no lo hace más lento.
La magia de un hash map es que buscar por clave es en la práctica O(1) — tiempo constante, sin importar cuántos elementos contenga. La clave pasa por una hash function que calcula dónde vive el valor, así que el map salta directo allí en lugar de buscar. Un millón de entradas o diez, encontrar una es igual de rápido. Por eso el hash map es el caballo de batalla: «encontrar por nombre» es lo que los programas hacen constantemente.
- Qué es
Una función que calcula, a partir de una clave, dónde está su valor en la tabla.
- Cómo funciona
La misma clave siempre da la misma casilla — así que la búsqueda nunca recorre las entradas.
- Ejemplo
hash("eve")→ casilla 3 → ahí está el número de Eve.- Relaciones
- El corazón de Hash Map y Set.
Set — presente o no, sin valores
Una lista de invitados en la puerta — no necesitas ningún detalle, solo un sí o un no: ¿está este nombre en la lista? Se comprueba al instante, sin recorrer nada.
Un primo cercano es el set: un hash map que guarda solo claves, sin valores, para responder rápido una pregunta — «¿está presente esto?». Comprobar pertenencia y evitar duplicados son ambos O(1). Cada vez que te veas recorriendo una lista para preguntar «¿ya vi esto?», un set convierte esa lenta comprobación O(n) en una instantánea.
- Qué es
- Una colección de claves únicas sin valores.
- Rápido
«¿Está aquí?» y añadir — O(1); los duplicados se descartan automáticamente.
- Ejemplo
seen = set()— saltarse los emails ya enviados; quitar los repetidos de una lista.- Cuándo usarlo
- Unicidad y comprobaciones rápidas de pertenencia.
- Relaciones
- Un Hash Map sin los valores.
Un hash map encuentra un valor por su clave en O(1) efectivo — el caballo de batalla para buscar por identificador. Su primo el set responde «¿está presente?» igual de rápido.
A veces el orden en que sacas las cosas importa tanto como lo que guardas. Dos estructuras formalizan los dos órdenes naturales — y la diferencia entre ellas es una sola regla intuitiva.
Stack — último en entrar, primero en salir (LIFO)
Una pila de platos: pones encima y tomas de encima, así que el último plato que dejaste es el primero que recoges.
Un stack sigue LIFO — last in, first out. Añades (push) y quitas (pop) por el mismo extremo, así que el elemento más reciente sale primero. Encaja con todo lo que se anida o se deshace en orden inverso: un historial de deshacer, el botón de atrás, la call stack de un programa. Ambas operaciones son O(1).
- Qué es
- Añadir y quitar solo por arriba.
- Rápido
pushypop— O(1).- Ejemplo
Deshacer en un editor, el botón de atrás del navegador, la call stack.
- Cuándo usarlo
- Necesitas «lo más reciente primero» o «deshacer lo último».
- Relaciones
- Espejo de Queue: los mismos datos, el otro extremo para sacar.
Queue — primero en entrar, primero en salir (FIFO)
Una cola en una taquilla: se atiende a la gente en el orden en que llegó, los recién llegados se ponen al final y se atiende al primero de la fila.
Una queue sigue FIFO — first in, first out. Añades por detrás (enqueue) y quitas por delante (dequeue), así que los elementos se procesan en orden de llegada. Es la estructura para la equidad y el orden: tareas por procesar, trabajos en espera, peticiones que atender por turno — justo la idea de la cola de mensajes del curso de queues. Como un stack, sus operaciones son O(1).
- Qué es
- Añadir por detrás, tomar por delante.
- Rápido
enqueueydequeue— O(1).- Ejemplo
Trabajos para workers en segundo plano, peticiones atendidas por turno.
- Cuándo usarlo
- El orden de llegada debe ser el orden de procesamiento.
- Relaciones
Espejo de Stack; a gran escala crece hasta ser una cola de mensajes.
La regla determina el comportamiento
El mismo montón de trabajo, dos puertas: una devuelve el elemento más reciente, la otra el más antiguo. La única elección de qué extremo cambia todo lo que viene después.
Los stacks y las queues guardan el mismo tipo de datos; la única diferencia es por qué extremo quitas. Elegir entre ellos es elegir el orden en que vuelve tu trabajo: inverso y anidado (stack) o justo y secuencial (queue). Una decisión pequeña con un efecto grande — acierta con el orden y el resto de la lógica encaja sola.
Un stack es último-en-entrar-primero-en-salir — deshacer, anidar, invertir. Una queue es primero-en-entrar-primero-en-salir — procesamiento justo y en orden. Mismos datos; el extremo por el que quitas es toda la diferencia.
Las últimas estructuras manejan datos que no son una línea plana sino que tienen forma — cosas que se ramifican en jerarquías o se conectan en redes. Así se modelan las relaciones.
Tree — una jerarquía
Un árbol genealógico o el organigrama de una empresa: una cosa arriba, que se ramifica hacia abajo en hijos, cada uno de los cuales puede ramificarse más — una estructura que se abre en abanico y nunca vuelve atrás.
Un tree organiza los datos como una jerarquía: una raíz (root) arriba, que se ramifica en nodos que tienen hijos, y así hacia abajo. Modela todo lo anidado — carpetas dentro de carpetas, comentarios y sus respuestas, un menú de categorías. La forma ramificada refleja el anidamiento del mundo real, lo que hace de los trees el ajuste natural siempre que tus datos son «cosas dentro de cosas». La mayoría de las jerarquías que encuentras en el software son trees por debajo.
- Qué es
- Una jerarquía: cada nodo puede tener hijos, pero nada vuelve atrás.
- Ejemplo
Carpetas y archivos, un hilo de comentarios, el árbol de categorías de una tienda.
- Cuándo usarlo
- Tus datos son «cosas dentro de cosas».
- Ojo
Si aprietas una jerarquía en una lista plana, tendrás que volver a pegarla a mano.
- Relaciones
Un tree ordenado y parejo es un Balanced tree; un tree es un caso particular de Graph.
Balanced tree — búsqueda en O(log n)
Encontrar un nombre en una guía telefónica abriendo por la mitad, luego por la mitad de la mitad derecha, una y otra vez — cada paso descarta la mitad de lo que queda.
Un tree bien organizado, equilibrado, da una propiedad potente: puedes buscar en él en O(log n) dividiendo el espacio a la mitad una y otra vez, como encuentras una palabra en un diccionario sin leer cada página. Esa forma logarítmica apenas crece aunque los datos exploten — mil millones de elementos son solo unos treinta pasos. Así mantienen las bases de datos las búsquedas rápidas a escala enorme: datos ordenados en forma de tree, buscados por mitades.
- Qué es
- Un tree ordenado con la misma profundidad por todos lados.
- Cómo funciona
En cada nodo se compara y se entra en una mitad — la otra mitad no se vuelve a mirar.
- Rápido
- Búsqueda — O(log n): mil millones de elementos ≈ 30 pasos.
- Ejemplo
- Índices de bases de datos: claves ordenadas en forma de tree.
- Relaciones
Un Tree especial; a diferencia de un Hash Map, conserva el orden.
Graph — una red de conexiones
Un mapa de ciudades unidas por carreteras, o de personas unidas por amistades — cosas conectadas entre sí en cualquier patrón, sin arriba ni abajo.
Un graph es la forma más general: nodos conectados por aristas (edges), en cualquier patrón, incluidos los ciclos. Modela redes — conexiones sociales, carreteras entre ciudades, enlaces entre páginas, dependencias entre tareas. Siempre que los datos sean en el fondo «cosas relacionadas con otras cosas» y no una jerarquía ni una línea, es un graph. Muchos problemas difíciles e interesantes — la ruta más corta, quién está conectado con quién — son problemas de graphs en el fondo.
- Qué es
Nodos y las aristas entre ellos — sin jerarquía, con ciclos permitidos.
- Ejemplo
Amigos en una red social, carreteras entre ciudades, enlaces entre páginas, dependencias entre tareas.
- Problemas típicos
- La ruta más corta; quién está conectado con quién.
- Cuándo usarlo
- Los datos son una red, no una línea ni una jerarquía.
- Relaciones
- Un Tree es un graph con una raíz y sin ciclos.
Un tree modela jerarquía y, equilibrado, busca en O(log n) por mitades. Un graph modela una red de conexiones. Úsalos cuando los datos tengan forma — anidada o enlazada.
Rara vez las construyes desde cero — tu lenguaje las trae. La habilidad está en elegir la correcta para el trabajo, y saber suficiente Big-O para notar una mala elección antes de que te muerda.
Dominant operation — elige por lo que más haces
Abasteces la cocina según lo que cocinas cada noche, no según lo que luce impresionante.
La decisión práctica casi siempre es: ¿qué operación haré más, y qué estructura vuelve rápida esa? ¿Buscar por clave, constantemente? Hash Map. ¿Necesitas las cosas en orden de llegada? Queue. ¿Sobre todo lees por posición? Array. Deja que la dominant operation elija la estructura, y acepta que será más lenta en lo que rara vez haces. Optimiza el caso común; la estructura es el cómo.
- Idea
- La operación más frecuente elige la estructura, no la costumbre.
- Ejemplo
Miles de búsquedas de usuario por ID por segundo → Hash Map, aunque se pierda el orden.
- Coste
Las operaciones raras se vuelven más lentas — una compensación deliberada.
- Relaciones
- Trade-off aplicado en la práctica.
Map before loop — usa un map antes que un bucle
Antes de buscar a alguien entre la multitud cara por cara, compruebas si hay una lista de invitados — la lista responde al instante lo que la búsqueda hace despacio.
Un movimiento muy común y muy eficaz: cuando te descubras recorriendo una lista para encontrar o emparejar cosas — un bucle O(n), o peor, un bucle O(n²) dentro de otro bucle — pregúntate si un hash map o un set podría volverlo instantáneo. Cambiar una búsqueda anidada por una consulta a un map es una de las aceleraciones más frecuentes en el mundo real. La solución suele ser la estructura, no un bucle más rápido.
- Problema
- Por cada elemento recorremos otra lista — O(n²), lento en producción.
- Idea
- Construye un Hash Map o Set una vez y luego busca en él en O(1).
- Ejemplo
En lugar de
users.find(u => u.id === order.userId)dentro de un bucle sobre pedidos — construye antesbyIdy usabyId[order.userId].- Coste
- Un poco de memoria para el map.
- Relaciones
- Convierte una Linear search dentro de un bucle en O(1).
- Qué operación domina — ¿búsqueda, orden, añadir/quitar?
- ¿Búsqueda por clave? Un Hash Map (o un Set, para presencia) es O(1).
- ¿Acceso por posición, con muchos añadidos? Encaja un Array.
- Importa el orden de salida — ¿lo más reciente (Stack) o lo más antiguo (Queue)?
- Datos anidados o en red — ¿un Tree o un Graph?
- A escala, ¿cuál es el Big-O — y ¿hay algo que sea O(n²) en secreto?
- Eliges la estructura por la operación que más haces, no por costumbre.
- Recurres a un Hash Map o Set en lugar de recorrer una lista para encontrar o deduplicar.
- Puedes nombrar el Big-O de tu camino crítico y no tienes ningún O(n²) oculto.
- Usas un Stack o Queue cuando importa el orden de salida, y un Tree o Graph cuando los datos tienen forma.
- Las cosas siguen rápidas al crecer los datos, porque elegiste la forma antes de que la escala la dejara al descubierto.
Cómo se conecta todo
Un árbol genealógico de herramientas: cada una cubre una debilidad de la anterior.
Big-O es la regla con que se mide cada estructura. Un Hash Map arregla el punto débil del Array (encontrar por valor); un Set es un Hash Map sin valores. Stack y Queue son una secuencia con la regla «tomar solo por un extremo». Un Tree añade jerarquía, y uno equilibrado busca en O(log n); un Graph levanta la última restricción y permite cualquier enlace.
Chuleta: los 16 conceptos
Fundamentos
- Data Structurecómo se disponen los datos
Una manera de organizar los datos para que tus operaciones comunes sean rápidas.
- Trade-offno hay almuerzo gratis
La fortaleza en una operación cuesta debilidad en otra.
Big-O
- Big-O notationcomplejidad asintótica
Cómo crece el trabajo al crecer el tamaño de datos n.
- Complexity classesO(1), O(log n), O(n), O(n²)
Constante, logarítmica, lineal, cuadrática. O(n²) es una alarma.
Array
- Arraylist
En orden, con index: O(1) por posición, O(n) para insertar en el medio.
- Linear searchscan
Encontrar por valor revisando uno por uno. O(n).
Hash map
- Hash Mapdiccionario, map
Clave → valor, búsqueda por clave en O(1) efectivo. Sin orden.
- Hash functionhashing
Calcula a partir de la clave dónde vive el valor — así no hay recorrido.
- Setmembership
Solo claves: «¿está aquí?» en O(1), sin duplicados.
Orden
- StackLIFO
Último en entrar, primero en salir. Deshacer, botón de atrás, call stack.
- QueueFIFO
Primero en entrar, primero en salir. Procesamiento en orden de llegada.
Forma
- Treejerarquía
Raíz, nodos, hijos, sin ciclos.
- Balanced treebalanced search tree
Búsqueda por mitades — O(log n). Cómo funcionan los índices de las bases de datos.
- Graphred
Nodos y aristas en cualquier patrón. Redes, rutas, dependencias.
Cómo elegir
- Dominant operationcaso común
La operación más frecuente elige la estructura.
- Map before loopbúsqueda en vez de recorrido
Antes de un recorrido anidado — construye un Hash Map o Set.
No las construyes desde cero — las eliges. Haz coincidir la estructura con la operación que más haces, usa un map antes que un bucle y deja que Big-O te avise antes que la escala.