Estructuras de Datos
Las estructuras de datos son formas de organizar y almacenar información en un programa de computadora para que pueda ser accedida y modificada de manera eficiente. Como programadores, es esencial entender las distintas estructuras de datos disponibles y saber cuándo aplicar cada una para optimizar el rendimiento y la utilidad de nuestros programas.
Una estructura de datos es una forma particular de organizar datos en la memoria de la computadora para que puedan ser usados de manera eficiente. Las estructuras de datos vienen en muchas formas, como arrays, listas, pilas, colas, grafos, árboles, hashes, etc.
Cada estructura organiza los datos de acuerdo a un modelo lógico específico y soporta operaciones eficientes para acceder, modificar, agregar y borrar elementos según ese modelo. Por ejemplo, un array organiza los elementos de manera secuencial en memoria para facilitar el acceso aleatorio por índices. Una lista enlazada conecta elementos en memoria usando “nodos” con referencias al siguiente nodo para facilitar la inserción y eliminación.
Al elegir la estructura de datos apropiada para la tarea a resolver, podemos escribir programas más eficientes y reducir la complejidad computacional, utilizando menos recursos como memoria y procesamiento.
Las estructuras de datos nos ayudan a:
Organizar grandes cantidades de datos para que sean más fáciles de acceder y modificar.
Modelar relaciones complejas entre datos, como con grafos y árboles.
Acceder y modificar datos de manera eficiente, optimizando el rendimiento.
Reutilizar código y estructuras de datos existentes en lugar de tener que reescribir soluciones desde cero.
Por ejemplo, un programa que debe almacenar miles de registros de usuarios se beneficia usando una estructura de datos hash para asociar cada usuario a datos como nombre, apellido, email, etc. De esta manera se pueden encontrar usuarios específicos muy rápido sin tener que iterar sobre toda la colección.
Otro ejemplo son los árboles de búsqueda binaria, que permiten encontrar elementos muy rápido en conjuntos ordenados de millones de elementos. Esto se logra descartando mitades del árbol a medida que se busca el elemento deseado.
Tipos de estructuras de datos
Existen muchos tipos de estructuras de datos. A continuación, se presentan algunas categorías útiles para clasificarlas.
Según relación entre elementos
Lineales: Los elementos se organizan secuencialmente uno después del otro. Por ejemplo, arrays, listas, pilas, colas.
No lineales: Los elementos se organizan en una jerarquía o grafo. Este es el caso de los árboles y grafos.
Según tipo de elementos
Homogéneas: Almacenan un solo tipo de datos. Por ejemplo, arrays en un lenguaje como Java.
Heterogéneas: Permiten diferentes tipos de datos. Objetos, registros son ejemplos de esta clasificación.
Según modo de acceso
Acceso secuencial: Sólo se puede acceder a los elementos en orden secuencial. Por ejemplo, listas enlazadas.
Acceso directo: Se puede acceder a cualquier elemento directamente por su posición. En este grupo se encuentran los arrays.
Acceso asociativo: Se accede a elementos por una clave asociada. Aquí se encuentran los diccionarios, hashes.
Según su funcionalidad
Arrays: Acceso rápido a elementos por índice pero difícil insertar/eliminar.
Listas: Fácil insertar/eliminar en cualquier posición pero acceso secuencial lento.
Pilas: Acceso LIFO (último en entrar, primero en salir). Útil para deshacer/rehacer.
Colas: Acceso FIFO (primero en entrar, primero en salir). Útil para procesamiento de eventos.
Árboles: Permiten modelar relaciones jerárquicas como con directorios de archivos o dependencias de tareas.
Grafos: Permiten modelar redes de interconexión como mapas, relaciones sociales, etc.
Hashes / Diccionarios: Asocian elementos con claves únicas para acceso ultra rápido.
Esta clasificación no es exhaustiva pero da una idea de la diversidad de estructuras de datos y sus diferentes propiedades que nos permiten modelar problemas complejos de manera eficiente.
Ejemplo
Veamos un ejemplo en Python para ver cómo se crea y utiliza una estructura de datos. Supongamos que queremos representar una cola de impresión donde las impresiones se procesan en orden de llegada (FIFO).
Primero, definimos una clase PrintQueue
para representar nuestra cola:
class PrintQueue:
def __init__(self):
self.items = []
def enqueue(self, item):
self.items.append(item)
def dequeue(self):
return self.items.pop(0)
def is_empty(self):
return len(self.items) == 0
Luego la utilizamos para agregar impresiones y procesarlas en orden:
print_queue = PrintQueue()
print_queue.enqueue("documento1.doc")
print_queue.enqueue("imagen3.jpg")
print_queue.enqueue("presentacion.ppt")
while not print_queue.is_empty():
next_item = print_queue.dequeue()
print(f"Imprimiendo {next_item}")
Esto imprimirá:
Imprimiendo documento1.doc
Imprimiendo imagen3.jpg
Imprimiendo presentacion.ppt
Con una estructura de datos como la cola implementamos la lógica FIFO de una forma sencilla y reutilizable. ¡Y esto es sólo una muestra, las posibilidades son infinitas!
Conclusión
Las estructuras de datos son herramientas fundamentales en programación que nos permiten organizar información de forma óptima para resolver problemas complejos. Conocer los distintos tipos de estructuras disponibles, como arrays, listas, pilas, colas, hashes, grafos y árboles, nos permite construir programas más eficientes. ¡Espero que esta introducción te haya dado algunos conocimientos y herramientas para comenzar a dominar este apasionante tema!
1 - Arreglos
Los arreglos (arrays en inglés) son estructuras de datos fundamentales en programación que permiten almacenar y organizar colecciones de datos del mismo tipo. Dominar el uso de arrays es esencial para cualquier programador.
Un array es una estructura de datos que representa un conjunto de elementos, los cuales se acceden a través de índices numéricos contiguos que van desde 0 hasta el tamaño del array menos 1. Los arrays proveen acceso rápido y directo a los elementos en base a su posición.
En lenguajes como Python y Ruby, los arrays se conocen como ’listas’ (lists). En Javascript se les conoce como ‘arreglos’ (arrays).
Los arrays son típicamente homogéneos, almacenando elementos del mismo tipo como enteros, cadenas, etc. Algunos lenguajes permiten arrays heterogéneos con valores de distintos tipos.

Creación de arrays
La manera de crear arrays varía según el lenguaje de programación:
MI_ARRAY = ["A", "B", "C"] # array literal
mi_array = list(range(5)) # array a partir de rango
Al crear un array literal se inicializan sus elementos directamente. Al construir un array vacío se especifica su tamaño pero sus elementos son inicializados con un valor default (0 para números, null para objetos, etc).
Acceder y modificar elementos
Los elementos individuales se acceden rápidamente por su índice utilizando corchetes []
:
my_array = ['a', 'b', 'c']
print(my_array[0]) # 'a'
print(my_array[2]) # 'c'
my_array[2] = 'z'
print(my_array[2]) # 'z'
Los índices comienzan en 0, por lo que en un array de tamaño N, los índices válidos están entre 0 y N-1.
Acceder a un índice inválido causa un error, por ejemplo, acceder al índice 3 en un array de tamaño 3. Esto se conoce como “index out of bounds”.
Recorrer un array
Podemos recorrer todos los elementos usando un ciclo for
:
letras = ['a', 'b', 'c']
for i in range(len(letras)):
print(letras[i])
Esto imprime cada elemento en orden. len()
devuelve la longitud total del array.
Otra forma es iterando directamente sobre los elementos:
letras = ['a', 'b', 'c']
for letra in letras:
print(letra)
Buscar en un array
Podemos buscar un elemento en un array mediante un ciclo y comparando elemento por elemento:
letras = ['a', 'b', 'c']
def buscar_en_array(array, elemento):
for i in range(len(array)):
if array[i] == elemento:
return i
return False
print(buscar_en_array(letras, 'b')) # 1
print(buscar_en_array(letras, 'z')) # False
Devuelve el índice si se encuentra o False
si no se encuentra.
Array multidimensional
Los arrays pueden tener más de una dimensión, por ejemplo matrices 2D, cubos 3D, etc.
Un array 2D se puede ver como una tabla con filas y columnas. Para acceder a un elemento se especifican dos índices, uno para la fila y otro para la columna:
matrix = [
[1, 2, 3],
[4, 5, 6]
]
print(matrix[0][2]) # 3
print(matrix[1][0]) # 4
Pueden tener más dimensiones, por ejemplo un array 3D para representar pixeles en una imagen.
Conclusión
Los arrays son estructuras de datos fundamentales en programación que proveen un acceso eficiente a elementos en memoria mediante índices numéricos. Tener un buen dominio de arrays, matrices y sus usos es indispensable para cualquier programador.
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!
2 - Mapas (Diccionarios)
Los mapas (también llamados hashes o diccionarios) son estructuras de datos que asocian claves con valores. Permiten un acceso ultra rápido a elementos mediante claves únicas. En Python se conocen como diccionarios.
Un diccionario, o mapa, consiste en una colección de pares clave-valor. La clave se utiliza para acceder al valor asociado. Las claves deben ser únicas dentro de un diccionario. Los valores pueden repetirse.

Operaciones principales
- Añadir/actualizar: Inserta un par clave-valor. Si la clave existía, su valor es reemplazado.
diccionario['clave'] = 'valor'
- Obtener valor: Acceder al valor dada una clave.
valor = diccionario['clave']
- Eliminar: Remueve un par clave-valor del diccionario.
- Recorrer: Iterar sobre las claves, valores o pares del diccionario.
for clave in diccionario:
print(clave, diccionario[clave]) # clave, valor
Creación de un diccionario o mapa
La sintaxis para crear mapas o diccionarios en Python es la siguiente:
diccionario_vacio = {}
persona = {
'nombre': 'Juan',
'edad': 25
}
Ejemplos de uso
Los diccionarios son útiles en muchos casos. A continuación se mencionan algunos de ellos.
Objetos y mapeos
Podemos modelar objetos y entidades con atributos clave-valor:
producto = {
'nombre': 'Smartphone',
'precio': 500,
'marca': 'XYZ'
}
Conteos y frecuencias
Contar ocurrencias de elementos en secuencias:
texto = "Hola mundo mundo"
frecuencias = {}
for palabra in texto.split():
if palabra in frecuencias:
frecuencias[palabra] += 1
else:
frecuencias[palabra] = 1
print(frecuencias)
# {'Hola': 1, 'mundo': 2}
Almacenar y acceder a datos
Como alternativa de alta performance a lists y arrays.
Conclusión
Los diccionarios son estructuras de datos versátiles gracias a su rápido acceso basado en claves únicas. Tienen usos en casi todos los programas, por lo que dominar diccionarios es indispensable en cualquier lenguaje.
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!
3 - Listas enlazadas
Las listas enlazadas (linked lists en inglés) son una estructura de datos dinámica y flexible que permite almacenar eficientemente colecciones de elementos. A diferencia de los arrays, las listas enlazadas no requieren reservar un tamaño fijo de antemano.
Una lista enlazada se compone de nodos
donde cada nodo tiene dos partes:
- Dato o información
- Referencia al siguiente nodo
Los nodos se organizan de forma secuencial, cada uno apuntando al siguiente. El último nodo apunta a nulo para indicar el final.
Esta estructura dinámica permite inserción y eliminación eficiente de nodos.
Tipos de listas enlazadas
Existen varios tipos:
- Simplemente enlazada: Cada nodo apunta al siguiente. Son útiles para colas (queues) y pilas (stacks).

- Doblemente enlazada: Cada nodo tiene referencia al siguiente y al anterior. Permiten recorrer en ambos sentidos.

- Circular: El último nodo apunta al primero formando un ciclo. Útiles para buffers circulares.

Operaciones comunes
Insertar: Agregar nodos al inicio, final o medio de la lista.
Eliminar: Quitar nodos por valor o posición.
Buscar: Encontrar nodos por valor recorriendo la lista.
Recorrer: Iterar los nodos accediendo por las referencias.
Implementación
Las listas enlazadas se pueden implementar de la siguiente manera:
Usa la clase ListNode
para representar nodos:
class ListNode:
def __init__(self, value):
self.value = value
self.next = None
Luego para crear y usar una lista se define una clase LinkedList con métodos para las operaciones.
class LinkedList:
def __init__(self):
self.head = None
def add_to_start(self, new_value):
new_node = ListNode(new_value)
new_node.next = self.head
self.head = new_node
def print(self):
current = self.head
while current != None:
print(current.value)
current = current.next
def search(self, searched_value):
current = self.head
while current != None:
if current.value == searched_value:
return True
current = current.next
return False
#...other methods
Con esta clase LinkedList
podemos crear una lista, agregar nodos, imprimirla, buscar elementos, etc.
Se podrían agregar otros métodos como insertar al final, eliminar por valor, obtener por índice, etc. Pero esto da una idea de cómo encapsular la funcionalidad de la lista enlazada en una clase. Como práctica, podés intentar agregar estos métodos por tu cuenta, ¡no dudes en dejar tus comentarios y contactarte si necesitas ayuda!
Ventajas y desventajas
Ventajas:
- Insertar y eliminar nodos es eficiente.
- No requiere definir tamaño fijo como los arrays.
- Estructura dinámica y flexible.
Desventajas:
- Mayor uso de memoria por tener que almacenar referencias.
- El acceso a elementos por índice es más costoso al ser secuencial.
Ejemplos de uso
- Implementar estructuras como pilas (stacks) y colas (queues).
- En listas doblemente enlazadas, recorrer la lista en el sentido ambos sentidos.
- Blockchains como la de Bitcoin.
- Reproducir elementos en orden como playlists de música.
Conclusión
Las listas enlazadas son una estructura de datos versátil para almacenar secuencias dinámicas de elementos. Tener un buen manejo de estas listas, sus operaciones y usos es indispensable para cualquier programador.
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!
4 - Pilas
Las pilas (stacks en inglés) son una estructura de datos abstracta que funciona bajo el principio LIFO (last in, first out), donde el último elemento en entrar es el primero en salir.
La naturaleza LIFO de las pilas se debe a que sólo se puede acceder y manipular el elemento superior. La operación de colocar un elemento sobre la pila se conoce como “push”, mientras que sacar un elemento de la pila es un “pop”. El funcionamiento LIFO provoca que el último elemento colocado en una pila sea el primero en ser retirado.

Operaciones principales
Las operaciones primarias que soporta una estructura de pila son:
- Push: agrega un elemento encima de la pila.
- Pop: saca el elemento de la pila que se encuentra en la cima.
- Peek: permite acceder al elemento de la cima sin sacarlo de la pila.
- isEmpty: consulta si la pila se encuentra vacía.
La mayoría de los lenguajes como Python y Java proveen implementaciones de pilas en sus librerías estándar.
Implementación
Una pila puede implementarse utilizando una lista enlazada de manera que cada node apunte al nodo anterior.
class Node:
def __init__(self, value):
self.value = value
self.previous = None
class Stack:
def __init__(self):
self.top = None
self.size = 0
def push(self, value):
new_node = Node(value)
if self.top is None:
self.top = new_node
else:
new_node.previous = self.top
self.top = new_node
self.size += 1
def pop(self):
if self.top is None:
return None
top_node = self.top
self.top = self.top.previous
self.size -= 1
return top_node.value
def peek(self):
if self.top is None:
return None
return self.top.value
def is_empty(self):
return self.top is None # Returns true if top is None
def __len__(self):
return self.size
def __str__(self):
values = []
current = self.top
while current:
values.append(str(current.value))
current = current.previous
return "\n".join(values)
Ejemplos de uso
Las pilas tienen muchos usos en programación:
Pila de ejecución (call stack): registra las llamadas a funciones pendientes de resolver. Implementa el comportamiento LIFO esperado.
Pila de navegador: permite volver atrás (undo) en el historial de navegación de forma similar a una pila LIFO.
Ejecución de expresiones matemáticas: mediante una pila se puede verificar paréntesis, corchetes, llaves, etc.
Algoritmos y estructuras de datos: como en el algoritmo quicksort y en la implementación de buses de datos (datapaths).
Conclusión
Las pilas son estructuras de datos versátiles gracias a su principio de funcionamiento LIFO. Tener un buen dominio de pilas, sus usos y aplicaciones es esencial en la ciencia de la computación.
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!
5 - Colas
Las colas (queues en inglés) son una estructura de datos abstracta que funciona bajo el principio FIFO (first in, first out), donde el primer elemento en entrar es también el primero en salir. Las colas se utilizan para ordenar elementos de forma que el que llega primero es procesado primero. Comprender su funcionamiento es esencial para cualquier programador.
La naturaleza FIFO (first in, first out) de las colas se debe a que sólo se puede acceder y manipular el elemento inicial. Cuando se agrega un elemento a la cola se conoce como “enqueue”, mientras que eliminar un elemento se denomina “dequeue”.
Esto hace que el primer elemento en ser añadido a la cola también sea el primero en ser retirado, de ahí su comportamiento FIFO.

Operaciones principales
Las operaciones básicas de una cola son:
- Enqueue: Agrega un elemento al final de la cola.
- Dequeue: Saca el elemento del frente de la cola.
- Peek: Obtiene el elemento al frente sin sacarlo.
- isEmpty: Consulta si la cola está vacía.
Implementación
Al igual que las pilas, las colas se pueden implementar usando listas enlazadas.
Se agrega al final y se saca del frente manteniendo referencias a ambos extremos.
class Node:
def __init__(self, value):
self.value = value
self.next = None
class Queue:
def __init__(self):
self.front = None
self.end = None
self.size = 0
def enqueue(self, value):
new_node = Node(value)
if self.end is None:
self.end = new_node
self.front = new_node
return
self.end.next = new_node
self.end = new_node
self.size += 1
def dequeue(self):
if self.is_empty():
return None
value = self.front.value
self.front = self.front.next
if self.front is None:
self.end = None
self.size -= 1
return value
def peek(self):
if self.is_empty():
return None
return self.front.value
def is_empty(self):
return self.front is None # Returns true if front is None
def __len__(self):
return self.size
def __str__(self):
values = []
current = self.front
while current:
values.append(str(current.value))
current = current.next
return "\n".join(values)
Ejemplos de uso
Algunos usos comunes de colas:
- Colas de impresión donde primero en entrar, primero en imprimir.
- Colas de tareas en sistemas operativos para orden de ejecución.
- Simulaciones donde se debe respetar orden de llegada como en bancos.
- Canales de mensajes como los de RabbitMQ o Kafka.
- Buffers circulares en audio para streaming.
Conclusión
Las colas son estructuras versátiles gracias a su principio FIFO. Tener un buen manejo de colas, implementación y aplicaciones reforzará tus habilidades como programador.
¡Felicitaciones por llegar hasta acá! Espero que este recorrido por el universo de la programación te haya resultado tan interesante como lo fue para mí al escribirlo.
Queremos conocer tu opinión, así que no dudes en compartir tus comentarios, sugerencias y esas ideas brillantes que seguro tenés.
Además, para explorar más allá de estas líneas, date una vuelta por los ejemplos prácticos que armamos para vos. Todo el código y los proyectos los encontrarás en nuestro repositorio de GitHub learn-software-engineering/examples.
Gracias por ser parte de esta comunidad de aprendizaje. ¡Seguí programando y explorando nuevas areas en este fascinante mundo del software!