English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

nu

invitado
1 / ?

¿Qué Hace que una Lista sea Útil

Listas: Tu Primera Estructura de Datos

Una lista (llamada matriz en algunos idiomas) almacena elementos en un orden específico. Puedes:

- Acceder a cualquier elemento por su posición: names[0] obtiene el primer elemento. O(1) — instantáneo.

- Añadir elementos al final: names.append("Zoe"). O(1) — instantáneo.

- Recorrer cada elemento en orden: for name in names. O(N) — visita cada elemento una vez.

Las listas conservan el orden en que colocas las cosas. El primer elemento sigue siendo el primero. El último sigue siendo el último.

# Una lista de mascotas
pets = ["cat", "dog", "cat", "fish", "dog"]

print(pets[0])   # "cat"  — O(1) instantáneo
print(pets[3])   # "fish" — O(1) instantáneo
print(len(pets)) # 5 — se mantienen los duplicados

Nota: "cat" aparece dos veces. "dog" aparece dos veces. Las listas permiten duplicados. Almacenan exactamente lo que les das, en el orden en que se lo diste.

Pero las listas tienen una debilidad. Mira qué sucede cuando preguntas: ¿Está "fish" en esta lista?

# Verificación de pertenencia — ¿Está "fish" en la lista?
if "fish" in pets:    # escanea ["cat", "dog", "cat", "fish"...] uno por uno
    print("encontrado!")   # tuvo que comprobar 4 elementos antes de encontrarlo

El ordenador verifica "cat" — no. "dog" — no. "cat" — no. "fish" — sí. Tuvo que escanear 4 elementos para obtener la respuesta. Con 1.000 elementos, podría escanear todos los 1.000. Esa verificación de pertenencia cuesta O(N).

Predice el Costo de Escaneo

Tienes una lista de 500 nombres de estudiantes en orden aleatorio.

Quieres verificar si "Zara" aparece en la lista.

students = ["Alex", "Maria", ..., "Zara", ...]  # 500 nombres
if "Zara" in students:
    print("inscrito")
¿Cuál es el caso mejor, peor y promedio de nombres que el ordenador verifica para encontrar "Zara"? ¿Qué clase de Big O describe esta operación? Explica tu razonamiento.

Cómo Funcionan los Conjuntos Diferentemente

Conjuntos: Diseñados para Preguntas de Membresía

Un conjunto almacena elementos únicos utilizando una técnica llamada hasheo. Cuando agregas un elemento, la computadora calcula un hash (una huella numérica) que le dice exactamente dónde almacenar ese elemento.

Cuando verificas la membresía, la computadora calcula el mismo hash y salta directamente al lugar correcto. Sin escaneo.

Lista vs Conjunto: cómo vive los datos en la memoria — lista escanea, conjunto salta

# Un conjunto de mascotas
mascotas = {"gato", "perro", "gato", "pez", "perro"}

print(mascotas)      # {"gato", "perro", "pez"} — eliminados duplicados
print(len(mascotas)) # 3 — solo elementos únicos

Toma nota de tres diferencias de las listas:

1. Sin duplicados. "gato" apareció dos veces en la entrada, pero el conjunto guarda solo una copia.

2. Sin orden garantizado. El conjunto puede imprimir elementos en cualquier disposición.

3. Sin acceso a índice. No se puede escribir mascotas[0] — los conjuntos no tienen posiciones.

El trueque: pierdes el orden y los duplicados. Ganas de O(1) pruebas de membresía — verificar si un elemento existe toma el mismo tiempo, ya sea que el conjunto tenga 5 elementos o 5 millones.

# Verificación de membresía — ¿"pez" está en el conjunto?
if "pez" in mascotas:    # hash("pez") → salta al bote → encontrado
    print("encontrado!")   # verificó exactamente 1 lugar, no 4

Conjunto vs Lista de Membresía

Tienes 500 nombres de estudiantes almacenados en un conjunto en lugar de una lista.

estudiantes = {"Alex", "Maria", ..., "Zara", ...}  # 500 nombres
si "Zara" en estudiantes:
    imprimir("inscrito")
¿Cuántos elementos verifica la computadora para determinar si "Zara" existe en un conjunto de 500 nombres? ¿Qué clase de Big O describe esto? ¿Por qué difiere de la versión de la lista?

Comparación Codo a Codo

Hoja de Honorarios de Operaciones

Costos de operaciones: lista vs conjunto - pertenencia, agregar, índice, deduplicar

Utiliza una lista cuando necesitas:

- Elementos en un orden específico (una lista de reproducción, una receta, una cola)

- Acceso por posición (items[3])

- Duplicados conservados ("gato" aparece 3 veces = 3 gatos)

Utiliza un conjunto cuando necesitas:

- Pruebas rápidas de pertenencia ("¿He visto esto antes?")

- Eliminación automática de duplicados

- Sin preocupación por el orden

Utiliza ambos cuando necesitas orden Y búsqueda rápida:

vistos = conjunto()     # O(1) pruebas de pertenencia
resultado = []      # conserva el orden de inserción
para elemento in datos:
    si elemento no en vistos:  # O(1) verificación
        vistos.agregar(elemento)    # O(1) agregar
        resultado.append(elemento)  # O(1) agregar
# Total: O(N) - una pasada, cada paso O(1)

Este "conjunto + lista" patrón te da lo mejor de ambos: resultados ordenados con detección rápida de duplicados.

Elige la Estructura Correcta

Tres problemas. Para cada uno, decide: lista, conjunto, o ambos?

1. Necesitas almacenar una lista de reproducción de canciones en el orden en que el usuario las agregó.

2. Necesitas verificar rápidamente si un nombre de usuario ya ha sido tomado.

3. Necesitas eliminar los correos electrónicos duplicados de una lista de correos, pero mantenerlos en el orden en que se registraron.

Para cada uno de los tres problemas, ¿cuál estructura de datos deberías usar (lista, conjunto, o ambos)? Explica por qué para cada uno.

El Problema de Deduplicación

Problema: Eliminar Duplicados, Mantener Orden

Recibes una lista de registros de correo electrónico. Algunas personas se registraron dos veces. Necesitas enviar un correo electrónico por persona, en el orden en que se registraron por primera vez.

Intento 1: solo lista (lento)

signups = ["alice@a.com", "bob@b.com", "alice@a.com", "carol@c.com"]
unique = []
for email in signups:
    if email not in unique:  # O(N) escaneo de la lista única
        unique.append(email)
# Funciona! Pero: O(N) verificación × N elementos = O(N²)

Con 100 registros, esto hace ~5,000 verificaciones. Con 10,000 registros: ~50,000,000 verificaciones.

Intento 2: conjunto + lista (rápido)

signups = ["alice@a.com", "bob@b.com", "alice@a.com", "carol@c.com"]
seen = set()
unique = []
for email in signups:
    if email not in seen:   # O(1) búsqueda por hash
        seen.add(email)     # O(1) agregar a conjunto
        unique.append(email) # O(1) agregar a lista
# O(1) verificación × N elementos = O(N)

Mismo resultado. Mismo orden. Pero: 10,000 registros ahora requieren ~10,000 verificaciones en lugar de ~50,000,000.

Calcular la Aceleración

La versión solo con lista se ejecuta en O(N²). La versión set+list se ejecuta en O(N).

You have 10,000 email signups to deduplicate.

¿Cuántas verificaciones aproximadas realiza cada versión en N=10,000? ¿Cuántas veces más rápido es la versión set+list? Muestre su cálculo.

Encontrar elementos comunes

Problema: encontrar elementos en ambas listas

Tienes dos listas de clases. Necesitas encontrar a los estudiantes inscritos en ambas clases.

Enfoque lento: bucles anidados O(N × M)

class_a = ["Alice", "Bob", "Carol", "Diana"]
class_b = ["Bob", "Diana", "Eve", "Frank"]
both = []
for student in class_a:        # N iteraciones
    if student in class_b:     # M escaneos cada vez
        both.append(student)
# O(N × M) - cada estudiante escaneado contra todos de class_b

Enfoque rápido: convertir una lista a un conjunto O(N + M)

class_a = ["Alice", "Bob", "Carol", "Diana"]
class_b_set = set(class_b)     # O(M) para crear
both = []
for student in class_a:        # N iteraciones
    if student in class_b_set: # O(1) cada vez
        both.append(student)
# O(N + M) - crear el conjunto una vez, revisar N veces en O(1) cada vez

O incluso más simple usando la intersección de conjuntos de Python incorporado:

both = set(class_a) & set(class_b)  # O(N + M)

El operador & encuentra todos los elementos que aparecen en ambos conjuntos.

Escribir para la velocidad

Una escuela tiene dos clubs con 200 miembros cada uno. Este código encuentra a los estudiantes en ambos clubs:

club_de_ajedrez = [...]   # 200 nombres
club_de_artes = [...]     # 200 nombres
clubes_comunes = []
for estudiante in club_de_ajedrez:
    if estudiante in club_de_artes:
        clubes_comunes.append(estudiante)
¿Cuál es el tiempo de ejecución O de este código? Redactelo para que corra más rápido utilizando un conjunto. ¿Cuál es el tiempo de ejecución O de tu versión mejorada? Explica la diferencia.

El Marco de Decisiones

Tu Marco de Decisiones de Estructuras de Datos

Cada vez que escribes código que almacena una colección de elementos, pregúntate:

1. ¿Necesito elementos en orden? → lista

2. ¿Necesito verificaciones de pertenencia rápidas? → conjunto

3. ¿Necesito ambos, orden y verificaciones rápidas? → conjunto + lista juntas

4. ¿Necesito elementos duplicados? → lista (los conjuntos eliminan duplicados)

---

La clave de esta lección: el mismo programa, resolviendo el mismo problema, puede ejecutarse 1,000× a 1,000,000× más rápido simplemente eligiendo la estructura de datos adecuada. Sin trucos ingeniosos. Sin matemáticas complicadas. Solo preguntar: ¿Qué operaciones realiza mi código con más frecuencia? Luego, elegir la estructura en la que esos costos O(1) en lugar de O(N).

Esta lección cubrió listas y conjuntos. Existen otras estructuras de datos (diccionarios, árboles, pilas) que resuelven otros problemas. El marco de toma de decisiones sigue siendo el mismo: entiende tus operaciones, luego elija la estructura que las haga baratas.

---

¿Quieres ir más allá? Nuestra lección sobre la notación O aborda los tipos de crecimiento y los cálculos de escalabilidad en detalle. Nuestro curso de unhamming cubre el defecto real (MOAD-0001) en el que esta solución exacta de lista a conjunto produjo una aceleración de velocidad de 1,000× en software de producción. Vea [El capítulo perdido: Complejidad algorítmica](/en/un/unhamming_algorithmic_complexity).

Depurá este código

Un estudiante escribió este código para contar cuántas palabras únicas aparecen en un libro:

words = book.split()           # lista de todas las palabras
unique_count = 0
checked = []
for word in words:             # N palabras
    if word not in checked:    # escanea la lista de verificado
        checked.append(word)
        unique_count += 1
print(unique_count)

El libro tiene 100,000 palabras.

¿Cuál es la complejidad de este código? ¿Por qué funciona lentamente en un libro grande? Redacte una solución que resuelva el mismo problema en O(N). Luego explique: ¿podrías resolver esto en una línea usando un conjunto? ¿Qué aspecto tendría eso?