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

nu

invitado
1 / ?
volver a las lecciones

Qué Hace Útil a una Lista

Listas: Tu Primera Estructura de Datos

Una lista (llamada matriz en algunos lenguajes) 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.

- Agregar 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 preservan el orden en que pones las cosas. El primer elemento se mantiene primero. El último se mantiene último.

# Una lista de mascotas
pets = ["gato", "perro", "gato", "pez", "perro"]

print(pets[0])   # "gato"  — O(1) instantáneo
print(pets[3])   # "pez" — O(1) instantáneo
print(len(pets)) # 5 — duplicados mantenidos

Observa: "gato" aparece dos veces. "perro" aparece dos veces. Las listas permiten duplicados. Almacenan exactamente lo que les das, en el orden en que lo diste.

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

# Verificación de membresía — ¿está "pez" en la lista?
if "pez" in pets:    # escanea ["gato", "perro", "gato", "pez"...] uno por uno
    print("¡encontrado!")   # tuvo que verificar 4 elementos antes de encontrarlo

La computadora verifica "gato" — no. "perro" — no. "gato" — no. "pez" — ¡sí! Escaneó 4 elementos para encontrar la respuesta. Con 1,000 elementos, podría escanear los 1,000. Esa verificación de membresía cuesta O(N).

Predice el Costo del Escaneo

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

Quieres verificar si "Zara" aparece en la lista.

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

Cómo Funcionan Diferentemente los Conjuntos

Conjuntos: Construidos para Preguntas de Membresía

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

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

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

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

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

Observa tres diferencias con las listas:

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

2. Sin orden garantizado. El conjunto podría imprimir elementos en cualquier arreglo.

3. Sin acceso por índice. No puedes escribir pets[0] — los conjuntos no tienen posiciones.

El compromiso: pierdes orden & duplicados. Ganas prueba de membresía O(1) — 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 — ¿está "pez" en el conjunto?
if "pez" in pets:    # hash("pez") → salta a bucket → encontrado
    print("¡encontrado!")   # verificó exactamente 1 lugar, no 4

Membresía: Conjunto vs Lista

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

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

Comparación Lado a Lado

Hoja de Trucos de Operaciones

Costos de operación: lista vs conjunto — membresía, agregar, índice, dedup

Usa una lista cuando necesites:

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

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

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

Usa un conjunto cuando necesites:

- Verificaciones de membresía rápidas ("¿he visto esto antes?")

- Remoción automática de duplicados

- Sin preocupación por el orden

Usa ambos cuando necesites orden & búsqueda rápida:

seen = set()     # Verificaciones de membresía O(1)
result = []      # preserva el orden de inserción
for item in data:
    if item not in seen:  # Verificación O(1)
        seen.add(item)    # O(1) agregar
        result.append(item)  # O(1) agregar al final
# Total: O(N) — un pase, cada paso O(1)

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

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 correos electrónicos duplicados de una lista de correo pero mantenerlos en el orden en que se registraron.

Para cada uno de los tres problemas, ¿qué estructura de datos debes 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 por persona, en el orden en que se registraron primero.

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:  # Escaneo O(N) de lista única
        unique.append(email)
# ¡Funciona! Pero: Verificación O(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()     # Verificaciones de membresía O(1)
unique = []
for email in signups:
    if email not in seen:   # Búsqueda de hash O(1)
        seen.add(email)     # O(1) agregar al conjunto
        unique.append(email) # O(1) agregar a la lista
# Verificación O(1) × N elementos = O(N)

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

Calcula la Aceleración

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

Tienes 10,000 registros de correo electrónico para deduplicar.

¿Cuántas verificaciones aproximadas realiza cada versión en N=10,000? ¿Cuántas veces más rápida es la versión conjunto+lista? Muestra tu cálculo.

Encontrando Elementos Comunes

Problema: Encuentra Elementos en Ambas Listas

Tienes dos listas de clases. Necesitas encontrar 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: convierte una lista a conjunto O(N + M)

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

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

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

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

Reescribe para Velocidad

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

chess_club = [...]   # 200 nombres
art_club = [...]     # 200 nombres
both_clubs = []
for student in chess_club:
    if student in art_club:
        both_clubs.append(student)
¿Cuál es el Big O de este código? Reescríbelo para ejecutarse más rápido usando un conjunto. ¿Cuál es el Big O de tu versión mejorada? Explica la diferencia.

El Marco de Decisión

Tu Marco de Decisión de Estructura de Datos

Cada vez que escribas código que almacena una colección de elementos, pregunta:

1. ¿Necesito elementos en orden? → lista

2. ¿Necesito verificaciones de membresía rápidas? → conjunto

3. ¿Necesito tanto orden como verificaciones rápidas? → conjunto + lista juntos

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

---

La idea clave de esta lección: el mismo programa, resolviendo el mismo problema, puede ejecutarse 1,000× a 1,000,000× más rápido solo eligiendo la estructura de datos correcta. Sin trucos inteligentes. Sin matemáticas complicadas. Solo preguntando: ¿qué operaciones hace mi código más a menudo? Luego elige la estructura donde esas operaciones cuestan O(1) en lugar de O(N).

Esta lección cubrió listas & conjuntos. Existen otras estructuras de datos (diccionarios, árboles, pilas) que resuelven otros problemas. El marco de decisión sigue siendo el mismo: entiende tus operaciones, luego elige la estructura que las hace económicas.

---

¿Quieres profundizar? Nuestra lección Big O cubre tasas de crecimiento & cálculos de escalado en detalle. Ve Big O: ¿Qué Tan Rápido Es Lo Suficientemente Rápido?. Nuestro curso unhamming cubre el defecto del mundo real (MOAD-0001) donde este arreglo exacto de lista a conjunto produjo una aceleración 1,000× en software de producción. Ve El Capítulo Faltante: Complejidad Algorítmica.

Depura 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 lista checked
        checked.append(word)
        unique_count += 1
print(unique_count)

El libro tiene 100,000 palabras.

¿Cuál es el Big O de este código? ¿Por qué se ejecuta lentamente en un libro grande? Reescríbelo para resolver el mismo problema en O(N). Luego explica: ¿podrías resolver esto en una línea usando un conjunto? ¿Qué aspecto tendría?