El análisis de complejidad describe cómo crece el tiempo de ejecución o el consumo de memoria de un algoritmo a medida que aumentan los datos. Las estructuras de datos proporcionan operaciones con costos diferentes; cuantificarlos mediante notación Big O nos permite tomar decisiones informadas sin depender de hardware específico.
Esta sección introduce el vocabulario esencial, explica por qué importan los distintos tipos de análisis y muestra comparaciones concretas entre operaciones frecuentes. No buscamos reemplazar demostraciones formales, sino ofrecer una guía clara para interpretar tablas de complejidad y detectar oportunidades de mejora.
La complejidad se expresa en función del tamaño de entrada n. Es una herramienta predictiva: al estimar el comportamiento asintótico podemos proyectar cómo escalará el sistema cuando cambie la carga.
Big O resume el crecimiento máximo de un algoritmo ignorando constantes. Su objetivo es clasificar comportamientos para comparar implementaciones sin ejecutar cada variante en todos los escenarios posibles.
El análisis no intenta dar tiempos exactos, sino caracterizar el ritmo de crecimiento. Por eso dos algoritmos O(n) pueden diferir en segundos, pero ambos escalarán de forma linear.
Además de Big O, la teoría utiliza otras notaciones para brindar contextos distintos:
Conocer estas variantes es útil para interpretar documentación de librerías y publicaciones académicas.
La estructura impone los limites físicos del algoritmo. Un array contiguo ofrece accesos estables pero inserciones costosas; una lista enlazada sacrifica localidad de memoria para ganar flexibilidad. El mismo algoritmo puede pasar de O(1) a O(n) según la representación.
ArrayList vs LinkedList en Java).Veamos cómo cambian las complejidades cuando variamos la estructura o el algoritmo.
Acceder a array[i] usa aritmética de punteros y se resuelve en O(1). En una lista enlazada debemos avanzar nodo por nodo hasta la posición deseada, lo que produce un costo O(n). Este contraste explica por qué las listas se reservan para escenarios donde importa más la modificación que la consulta aleatoria.
La búsqueda lineal compara cada elemento hasta encontrar la coincidencia (peor caso O(n)). La búsqueda binaria, en listas ordenadas, divide el espacio por la mitad en cada paso y alcanza un tiempo O(log n). A continuación se muestra una comparación en pseudocódigo:
def busqueda_lineal(datos, clave):
for valor in datos:
if valor == clave:
return True
return False # O(n)
def busqueda_binaria(datos, clave):
izq, der = 0, len(datos) - 1
while izq <= der:
medio = (izq + der) // 2
if datos[medio] == clave:
return True
if clave < datos[medio]:
der = medio - 1
else:
izq = medio + 1
return False # O(log n)
El requisito de tener los datos ordenados justifica el esfuerzo extra de ordenar previamente o mantener una estructura ordenada (como un árbol balanceado). En colecciones pequeñas la diferencia es mínima, pero con millones de elementos determina la viabilidad del sistema.