Existen docenas de estructuras de datos, pero se agrupan en familias según su forma de organizar los elementos y las operaciones que priorizan. Conocer esta clasificación permite seleccionar rápidamente una opción viable antes de entrar en detalles de implementación.
En este tema repasamos cuatro grandes categorías: estructuras lineales, no lineales, basadas en hashing y estructuras avanzadas. Cada familia está vinculada con patrones de acceso característicos y compromisos entre velocidad, memoria y complejidad.
Las estructuras lineales organizan los elementos en una sola dirección lógica. Cada dato tiene un anterior y un siguiente (excepto los extremos), lo que facilita recorridos secuenciales y operaciones que dependen de la posición.
Un array (arreglo) almacena elementos en posiciones contiguas de memoria. Su ventaja principal es el acceso directo: conocer el índice permite leer o escribir en tiempo constante. Esto lo hace ideal para datos de tamaño fijo o cuando las lecturas superan ampliamente a las inserciones.
Una lista enlazada conecta nodos a través de referencias. Puede ser simple (sólo un enlace al siguiente), doble (enlaces al siguiente y anterior) o circular. Las listas permiten crecer de forma dinámica y realizar inserciones o eliminaciones sin mover grandes bloques.
La pila es una estructura LIFO donde el último elemento insertado es el primero en salir. Se maneja con operaciones push (apilar) y pop (desapilar). Internamente puede basarse en un array o en una lista, pero el contrato LIFO siempre se mantiene.
La cola es una estructura FIFO en la que el primer elemento que entra es el primero en salir. Se implementa comúnmente con arrays circulares o listas con referencias a la cabecera y la cola.
Las estructuras no lineales permiten que un elemento tenga múltiples relaciones con otros, formando jerarquías, redes o entramados complejos. Son apropiadas para representar dominios donde el orden secuencial no alcanza.
Un árbol es una colección de nodos conectados por aristas orientadas que parten de un nodo raíz. Cada nodo puede tener cero o más hijos, lo que permite modelar jerarquías como directorios, catálogos o decisiones.
Un grafo consiste en un conjunto de vértices conectados mediante aristas. Puede ser dirigido o no dirigido, con pesos, etiquetas o restricciones específicas. Representa sistemas donde las relaciones importan tanto como los elementos.
Un trie es un árbol especializado para almacenar cadenas o secuencias donde cada nivel representa un carácter o elemento. Comparte prefijos y facilita búsquedas por coincidencia parcial, lo cual es crucial en sistemas de autocompletado o validación incremental.
En lugar de depender del orden o de las relaciones jerárquicas, estas estructuras utilizan funciones matemáticas para transformar claves en posiciones de memoria. Buscan minimizar colisiones y proporcionar operaciones cercanas a tiempo constante.
Una función hash toma una clave y genera un código numérico dentro de un rango fijo. Para que una estructura basada en hashing funcione, la función debe ser rápida de calcular y distribuir uniformemente las claves.
Los mapas o diccionarios almacenan pares clave-valor y permiten recuperar un elemento a partir de su clave en tiempo cercano a constante. En muchos lenguajes son la estructura por defecto para representar configuraciones, catálogos o resultados de consultas.
Cuando los requisitos son muy específicos (operaciones logarítmicas garantizadas, actualizaciones por rangos, consultas sobre subsecuencias), recurrimos a estructuras avanzadas. Aunque su implementación es más compleja, ofrecen garantías adicionales imprescindibles en algorítmica competitiva, motores de bases de datos o sistemas de tiempo real.
Los heaps son arboles casi completos donde cada nodo mantiene una relación de prioridad respecto a sus hijos. Permiten obtener el máximo o mínimo en tiempo constante e insertar en tiempo logarítmico, lo que los vuelve ideales para colas de prioridad y algoritmos como Dijkstra.
Los segment trees dividen un arreglo en segmentos o intervalos y almacenan información agregada de cada segmento (suma, mínimo, máximo). Hacen posible responder consultas sobre rangos y actualizar valores individuales en tiempo logarítmico.
Tambien conocidos como Binary Indexed Trees, los Fenwick trees ofrecen una alternativa más ligera a los segment trees para consultas y actualizaciones de prefijos acumulados. Su implementación se apoya en operaciones bit a bit para determinar los intervalos relevantes.
El Union-Find mantiene conjuntos disjuntos y soporta operaciones para unir conjuntos y encontrar el representante de cada elemento. Es crucial en algoritmos de componentes conectados o en la detección de ciclos.