Notação Big-O
Estruturas de Dados & Algoritmos • Módulo 1: Complexidade • Codding — material para estudo offline
Aluno: ______________________________ Data: ____ / ____ / ______
Nesta aula, você vai compreender Notação Big-O com explicação progressiva, exemplo resolvido, checagens e prática. Nada de apenas copiar: cada etapa prepara a próxima.
Objetivos da aula
- Explicar Notação Big-O com suas próprias palavras
- Reconhecer as partes importantes em um exemplo real
- Identificar e corrigir os erros mais frequentes
- Aplicar o conteúdo em uma atividade prática
Pré-requisito: Leia a aula anterior do módulo se algum termo parecer novo. Você pode testar o exemplo antes de continuar. • Duração: 50–75 min
Explicação passo a passo
1. O que é Notação Big-O
Big-O descreve como o custo de um algoritmo cresce quando a entrada cresce. Não mede segundos: mede a forma da curva — e é isso que decide se seu código aguenta 10 mil usuários.
Checkpoint: O que O(n²) costuma indicar no código? — Resposta: Laços aninhados sobre a mesma entrada
2. Como pensar antes de fazer
O(1) não depende do tamanho (acessar índice, ler chave de dicionário). O(n) percorre tudo uma vez. O(n log n) é o teto dos bons algoritmos de ordenação. O(n²) são laços aninhados — aceitável em 100 itens, desastre em 100 mil.
Dica: Não decore a forma. Tente explicar com suas palavras qual problema “Notação Big-O” resolve.
3. Exemplo completo, do início ao resultado
Constantes e termos menores somem: 3n + 50 é O(n). O que importa é o termo dominante, porque é ele que manda quando o dado cresce.
# O(n): um laço sobre a entrada
def tem_duplicado_lento(lista):
for i in range(len(lista)):
for j in range(i + 1, len(lista)): # laço dentro de laço => O(n²)
if lista[i] == lista[j]:
return True
return False
# O(n): um conjunto resolve com uma passada
def tem_duplicado(lista):
vistos = set()
for item in lista:
if item in vistos: # busca O(1)
return True
vistos.add(item)
return False| Trecho | O que faz |
|---|
# O(n): um laço sobre a entrada | Comentário de orientação: documenta a intenção sem executar uma ação. |
def tem_duplicado_lento(lista): | Cria uma estrutura reutilizável para organizar comportamento. |
for i in range(len(lista)): | Percorre ou transforma uma sequência de valores. |
for j in range(i + 1, len(lista)): # laço dentro de laço => O(n²) | Percorre ou transforma uma sequência de valores. |
if lista[i] == lista[j]: | Decide qual caminho será executado de acordo com uma condição. |
return True | Produz ou devolve o resultado que permite conferir a execução. |
return False | Produz ou devolve o resultado que permite conferir a execução. |
# O(n): um conjunto resolve com uma passada | Comentário de orientação: documenta a intenção sem executar uma ação. |
Dica: As duas funções fazem a mesma coisa. A segunda troca tempo por um pouco de memória e sai de O(n²) para O(n).
Checkpoint: Por que 3n + 50 é considerado O(n)? — Resposta: Porque o que importa é o termo dominante quando a entrada cresce
4. Do detalhe para o todo
Custo de memória conta também. Criar uma cópia da lista dentro de um laço é O(n²) de tempo e memória escondido em código que parece inocente.
5. Erros comuns e como corrigi-los
1. Colocar uma busca em lista (O(n)) dentro de um laço — vira O(n²) sem você perceber.
2. Otimizar antes de medir: o gargalo quase nunca está onde você acha.
3. Confundir 'rápido na minha máquina com 10 itens' com 'escala'.
Dica: Quando algo der errado, compare uma diferença por vez com o exemplo. Evite mudar várias partes ao mesmo tempo.
Prática guiada
1. 1. Faça funcionar
Laboratório de Notação Big-O: execute o exemplo, identifique entrada, processamento e saída e altere pelo menos um valor para observar como o resultado muda.
# O(n): um laço sobre a entrada
def tem_duplicado_lento(lista):
for i in range(len(lista)):
for j in range(i + 1, len(lista)): # laço dentro de laço => O(n²)
if lista[i] == lista[j]:
return True
return False
# O(n): um conjunto resolve com uma passada
def tem_duplicado(lista):
vistos = set()
for item in lista:
if item in vistos: # busca O(1)
return True
vistos.add(item)
return FalseDica: Volte ao exemplo resolvido e compare a estrutura. Colocar uma busca em lista (O(n)) dentro de um laço — vira O(n²) sem você perceber.
2. 2. Preveja antes de testar
Sem executar, explique qual resultado o exemplo de Notação Big-O deve produzir e qual parte é responsável por ele.
Minha previsão:
A parte responsável pelo resultado:
Como vou conferir:
Dica: Use o modelo mental da aula: entrada, transformação e evidência observável.
3. 3. Encontre e corrija o erro
Use o exemplo de Notação Big-O, identifique um risco citado na aula e registre ou implemente a correção.
# O(n): um laço sobre a entrada
def tem_duplicado_lento(lista):
for i in range(len(lista)):
for j in range(i + 1, len(lista)): # laço dentro de laço => O(n²)
if lista[i] == lista[j]:
return True
return False
# O(n): um conjunto resolve com uma passada
def tem_duplicado(lista):
vistos = set()
for item in lista:
if item in vistos: # busca O(1)
return True
vistos.add(item)
return FalseDica: Colocar uma busca em lista (O(n)) dentro de um laço — vira O(n²) sem você perceber.
4. 4. Aplique em um caso novo
Crie uma variação própria usando Notação Big-O. Mude os dados ou o cenário e demonstre que o resultado continua correto.
# O(n): um laço sobre a entrada
def tem_duplicado_lento(lista):
for i in range(len(lista)):
for j in range(i + 1, len(lista)): # laço dentro de laço => O(n²)
if lista[i] == lista[j]:
return True
return False
# O(n): um conjunto resolve com uma passada
def tem_duplicado(lista):
vistos = set()
for item in lista:
if item in vistos: # busca O(1)
return True
vistos.add(item)
return FalseDica: Mantenha a estrutura que já funcionou e altere uma decisão por vez.
Revisão
- Big-O descreve como o custo de um algoritmo cresce quando a entrada cresce. Não mede segundos: mede a forma da curva — e é isso que decide se seu código aguenta 10 mil usuários.
- As duas funções fazem a mesma coisa. A segunda troca tempo por um pouco de memória e sai de O(n²) para O(n).
- Evite: Colocar uma busca em lista (O(n)) dentro de um laço — vira O(n²) sem você perceber.
- Evite: Otimizar antes de medir: o gargalo quase nunca está onde você acha.
Exemplo comentado
# O(n): um laço sobre a entrada
def tem_duplicado_lento(lista):
for i in range(len(lista)):
for j in range(i + 1, len(lista)): # laço dentro de laço => O(n²)
if lista[i] == lista[j]:
return True
return False
# O(n): um conjunto resolve com uma passada
def tem_duplicado(lista):
vistos = set()
for item in lista:
if item in vistos: # busca O(1)
return True
vistos.add(item)
return FalseAs duas funções fazem a mesma coisa. A segunda troca tempo por um pouco de memória e sai de O(n²) para O(n).
Erros comuns
- Colocar uma busca em lista (O(n)) dentro de um laço — vira O(n²) sem você perceber.
- Otimizar antes de medir: o gargalo quase nunca está onde você acha.
- Confundir 'rápido na minha máquina com 10 itens' com 'escala'.
Quiz e gabarito completo
1. O que O(n²) costuma indicar no código?
- a) Uso de recursão
- b) Laços aninhados sobre a mesma entrada
- c) Muitas variáveis
- d) Uso de dicionário
Gabarito: b) Laços aninhados sobre a mesma entrada — Cada item da entrada sendo comparado com todos os outros gera crescimento quadrático.
2. Por que 3n + 50 é considerado O(n)?
- a) Porque 50 é pequeno
- b) Porque o que importa é o termo dominante quando a entrada cresce
- c) Porque n é sempre maior que 50
- d) Porque constantes não existem
Gabarito: b) Porque o que importa é o termo dominante quando a entrada cresce — Big-O descreve a forma do crescimento, não o valor exato.
Codding • Estruturas de Dados & Algoritmos • Notação Big-O • Prova final e certificado em codding.lovable.app/cursos/estruturas-de-dados/prova