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

nu

visitante
1 / ?

O que Faz de Uma Lista Útil

Listas: Sua Primeira Estrutura de Dados

Uma lista (chamada de matriz em alguns idiomas) armazena itens em uma determinada ordem. Você pode:

- Acessar qualquer item pelo seu lugar: names[0] pega o primeiro item. O(1) — instantâneo.

- Adicionar itens no final: names.append("Zoe"). O(1) — instantâneo.

- Percorrer todos os itens na ordem: for name in names. O(N) — visita cada item uma vez.

Listas preservam a ordem em que você coloca as coisas. O primeiro item fica no primeiro lugar. O último fica no último.

# Uma lista de pets
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 — duplicatas mantidas

Observação: "cat" aparece duas vezes. "dog" aparece duas vezes. Listas permitem duplicatas. Elas armazenam exatamente o que você dá, na ordem que deu.

Mas as listas têm uma fraqueza. Veja o que acontece quando você pergunta: esse "fish" está na lista?

# Verificação de associação — o "fish" está na lista?
if "fish" in pets:    # varre ["cat", "dog", "cat", "fish"...] um por um
    print("encontrado!")   # teve que verificar 4 itens antes de encontrá-lo

O computador verifica "cat" — não. "dog" — não. "cat" — não. "fish" — sim! Ele escaneou 4 itens para encontrar a resposta. Com 1.000 itens, pode ser que ele escaneie todos os 1.000. Essa verificação de associação custa O(N).

Adivinhe o Custo de Escaneamento

Você tem uma lista de 500 nomes de estudantes em ordem aleatória.

Deseja verificar se "Zara" está na lista.

students = ["Alex", "Maria", ..., "Zara", ...]  # 500 nomes
if "Zara" in students:
    print("matriculado")
Qual é o melhor caso, o pior caso e o número médio de nomes que o computador verifica para encontrar "Zara"? Qual classe Big O descreve essa operação? Explique sua razão.

Como os Conjuntos Funcionam Diferentemente

Conjuntos: Feitos para Perguntas de Membro

Um conjunto armazena itens únicos usando uma técnica chamada hashing. Quando você adiciona um item, o computador calcula um hash (uma impressão digital numérica) que diz a ele exatamente onde armazenar esse item.

Quando você verifica o membro, o computador calcula o mesmo hash e pula diretamente para o lugar certo. Sem escaneamento.

Lista vs Conjunto: como os dados vivem na memória - lista escaneia, conjunto pula

# Um conjunto de pets
pets = {"gato", "cão", "gato", "peixe", "cão"}

print(pets)      # {"gato", "cão", "peixe"} — duplicatas removidas
print(len(pets)) # 3 — apenas itens únicos

Observe três diferenças das listas:

1. Sem duplicatas. "gato" apareceu duas vezes na entrada, mas o conjunto mantém apenas uma cópia.

2. Sem ordem garantida. O conjunto pode imprimir itens em qualquer arranjo.

3. Sem acesso por índice. Você não pode escrever pets[0] - conjuntos não têm posições.

O trade-off: você perde a ordem e duplicatas. Ganha O(1) teste de membro - verificar se um item existe leva o mesmo tempo, independentemente de o conjunto ter 5 itens ou 5 milhões.

# Verificação de membro - o "peixe" está no conjunto?
if "peixe" in pets:    # hash("peixe") → salto para o bucket → encontrado
    print("encontrado!")   # verificado exatamente 1 lugar, não 4

Conjunto vs Lista de Membros

Você tem 500 nomes de estudantes armazenados em um conjunto em vez de uma lista.

students = {"Alex", "Maria", ..., "Zara", ...}  # 500 nomes
se "Zara" está na lista de alunos:
    print("matriculado")
Quantos itens o computador verifica para determinar se "Zara" existe em um conjunto de 500 nomes? Qual classe Big O descreve isso? Por que difere da versão da lista?

Comparação Lado a Lado

Cartão de Operações

Custos de operação: lista vs conjunto - membro, adicionar, índice, dedup

Use uma lista quando você precisar:

- Itens em um determinado order (uma playlist, uma receita, uma fila)

- Acesso por posição (items[3])

- Duplicatas preservadas ("gato" aparece 3 vezes = 3 gatos)

Use um conjunto quando você precisar:

- Verificações rápidas de membro ("eu já vi isso antes?")

- Remoção automática de duplicatas

- Sem preocupação com a ordem

Use ambos quando precisar de order E busca rápida:

seen = set()     # O(1) verificações de membro
result = []      # preserva a ordem de inserção
for item in data:
    if item not in seen:  # O(1) verificação
        seen.add(item)    # O(1) adicionar
        result.append(item)  # O(1) anexar
# Total: O(N) - uma passagem, cada etapa O(1)

Este "conjunto + lista" padrão dá a você o melhor de ambos: resultados ordenados com detecção rápida de duplicatas.

Escolha a Estrutura Correta

Três problemas. Para cada um, decida: lista, conjunto ou ambos?

1. Você precisa armazenar uma playlist de músicas na ordem que o usuário adicionou.

2. Você precisa verificar rapidamente se um nome de usuário já foi utilizado.

3. Você precisa remover emails duplicados de uma lista de correio, mas mantê-los na ordem em que se cadastraram.

Para cada um dos três problemas, qual estrutura de dados você deve usar (lista, conjunto ou ambos)? Explique por que para cada um.

The Deduplication Problem

Problema: Remover Duplicatas, Manter a Ordem

Você recebe uma lista de inscrições de email. Algumas pessoas se cadastraram duas vezes. Você precisa enviar um email por pessoa, na ordem em que se cadastraram pela primeira vez.

Tentativa 1: somente lista (lenta)

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) escaneamento da lista única
        unique.append(email)
# Funciona! Mas: O(N) verificação × N itens = O(N²)

Com 100 inscrições, isso faz ~5.000 verificações. Com 10.000 inscrições: ~50.000.000 verificações.

Tentativa 2: conjunto + lista (rápida)

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) busca hash
        seen.add(email)     # O(1) adicionar ao conjunto
        unique.append(email) # O(1) adicionar à lista
# O(1) verificação × N itens = O(N)

Mesmo resultado. Mesma ordem. Mas: 10.000 inscrições agora requerem ~10.000 verificações em vez de ~50.000.000.

Calculate the Speedup

A versão apenas com lista tem complexidade de O(N²). A versão set+list tem complexidade de O(N).

You have 10,000 email signups to deduplicate.

Quantos chequeios aproximados cada versão realiza ao N=10.000? Em quanto a versão set+list é mais rápida? Mostre seu cálculo.

Encontrando Elementos Comuns

Problema: Encontrar Itens em Duas Listas

Você tem duas listas de alunos. Precisa encontrar os estudantes matriculados em ambas as classes.

Abordagem Lenta: laços aninhados O(N × M)

class_a = ["Alice", "Bob", "Carol", "Diana"]
class_b = ["Bob", "Diana", "Eve", "Frank"]
both = []
for student in class_a:        # N iterações
    if student in class_b:     # M scans cada vez
        both.append(student)
# O(N × M) — cada aluno escaneado contra todos de class_b

Abordagem Rápida: converta uma lista em um 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 iterações
    if student in class_b_set: # O(1) cada vez
        both.append(student)
# O(N + M) — construa o conjunto uma vez, verifique N vezes com O(1) cada

Ou mesmo mais simples usando a interseção de conjuntos do Python:

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

O operador & encontra todos os itens que aparecem em ambos os conjuntos.

Escreva para a Velocidade

Uma escola tem dois clubes com 200 membros cada. Este código encontra estudantes em ambos os clubes:

chess_club = [...]   # 200 names
art_club = [...]     # 200 names
both_clubs = []
for student in chess_club:
    if student in art_club:
        both_clubs.append(student)
Qual é o Big O desse código? Rewrite it to run faster using a set. Qual é o Big O da sua versão aprimorada? Explicar a diferença.

O quadro de decisão

Seu quadro de decisão de estrutura de dados

Toda vez que você escrever código que armazena uma coleção de itens, pergunte:

1. Eu preciso dos itens em ordem? → lista

2. Eu preciso de verificações de associação rápidas? → conjunto

3. Eu preciso de ambos, ordem E verificações rápidas? → conjunto + lista juntos

4. Eu preciso de duplicatas? → lista (conjuntos descartam duplicatas)

---

A chave de insight desta lição: o mesmo programa, resolvendo o mesmo problema, pode correr 1.000× a 1.000.000× mais rápido apenas escolhendo a estrutura de dados certa. Nenhuma trapaça. Nenhuma matemática complicada. Só perguntar: o que operações meu código faz com mais frequência? Então escolher a estrutura onde essas operações custam O(1) em vez de O(N).

Esta lição cobriu listas e conjuntos. Existem outras estruturas de dados (dicionários, árvores, pilhas) que resolvem outros problemas. O framework de decisão permanece o mesmo: entenda suas operações e então escolha a estrutura que as torna baratas.

---

Quer ir mais fundo? A nossa aula sobre Big O aborda taxas de crescimento e cálculos de escalonamento em detalhes. Confira [Big O: Quanto É O Suficiente?](/en/nu/cs_algorithms_big_o_notation). O nosso curso Unhamming cobre o defeito real-world (MOAD-0001) onde essa exata correção de lista-conjunto produziu uma aceleração de velocidade de 1.000× em softwares de produção. Confira [O Capítulo Faltante: Complexidade Algorítmica](/en/un/unhamming_algorithmic_complexity).

Depure Este Código

Um aluno escreveu esse código para contar quantas palavras únicas aparecem em um livro:

words = book.split()           # lista de todas as palavras
unique_count = 0
checked = []
for word in words:             # N palavras
    if word not in checked:    # escaneia a lista checked
        checked.append(word)
        unique_count += 1
print(unique_count)

O livro tem 100.000 palavras.

Qual é a Big O desse código? Por que ele corre lentamente em um livro grande? Rewritte ele para resolver o mesmo problema em O(N). Então explique: você poderia resolver isso em uma linha usando um conjunto? O que isso seria?