emrevarol.com Materials
A2SV
def solve(problem): understand(problem) manual_solve(example) design_algorithm() attack_own_idea() implement() test_comprehensively() simplify() return solution class Interview: def __init__(self): self.steps = 7 self.clarity = True self.speed = False # The Playbook for step in range(1, 8): execute(step) communicate(step) verify(step)

7 Passos para uma
Resolução de Problemas Altamente Eficaz

O Manual da Entrevista

A2SV - Emre Varol

Por que um Manual?

  • Entrevistas criam pressão
  • Pressão destrói o pensamento aleatório
  • Um manual cria consistência
  • Consistência supera brilhantismo ocasional
Aleatório
↗ ↙ ↖ ↘
↓ ↑ → ←
↙ ↗ ↘ ↖
Estruturado
1. Entender 2. Manual 3. Projetar 4. Atacar 5. Codificar 6. Testar 7. Limpar

Não apenas resolver problemas -
mas resolvê-los de forma confiável sob pressão

Melhor julgamento
Menos erros evitáveis
Comunicação mais clara
Mais aprovações em entrevistas

Os 7 Passos

1
Entenda o problema
2
Resolva o problema manualmente
3
Elabore uma ideia de solução
4
Ataque sua própria ideia
5
Implemente
6
Teste de forma abrangente
7
Simplifique e limpe o código

O Maior Erro do Candidato

Começar a programar cedo demais

Consequências

  • Entendimento errado
  • Algoritmo errado
  • Complexidade errada
  • Tempo desperdiçado
01
Entenda o Problema
  • Leia devagar
  • Processe cada frase
  • Extraia as restrições
  • Reformule com suas próprias palavras

Passo 1: Bom vs Ruim

Ruim

  • Lê o problema por cima
  • Supõe o que está sendo pedido
  • Começa a programar após ler uma vez

Bom

  • Reformula entrada e saída
  • Identifica restrições
  • Confirma exatamente o que deve ser retornado

Exemplo de Problema: Two Sum

Dado um array de inteiros nums e um inteiro target, retorne os índices dos dois números cuja soma é igual ao target. Assuma que exatamente uma resposta válida existe e que o mesmo elemento não pode ser usado duas vezes.
target = 9
2
0
7
1
11
2
15
3
2 + 7 = 9 → return [0, 1]

Bom Passo 1 no Two Sum

Candidato diz:

  • Entrada: array + target
  • Saída: dois índices
  • Retornamos posições, não valores
  • Exatamente uma resposta existe
  • Não pode reutilizar o mesmo elemento
Nenhuma ambiguidade restante.
Restrições importantes capturadas.

Passo 1 Ruim no Two Sum

Comportamento Ruim do Candidato

  • "Eu só preciso de dois números que somem"
  • Esquece índices vs valores
  • Ignora a restrição de exatamente-uma-resposta
  • Ignora a regra de não-reutilizar-o-mesmo-elemento

Código que parece correto ainda pode estar errado.

02
Resolva o Problema Manualmente
"Se você não consegue fazer à mão, não consegue automatizar."
  • Comece com o exemplo
  • Acompanhe suas decisões
  • Procure por repetição
  • Extraia o padrão

Passo 2: Bom vs Ruim

Ruim

  • Pula da leitura para o código
  • Diz "Acho que sei o truque"
  • Nunca verifica um exemplo pequeno

Bom

  • Trabalha com a entrada de exemplo
  • Percebe ações repetitivas
  • Extrai quais informações ajudariam

Passo a Passo Manual: Two Sum

nums = [2, 7, 11, 15], target = 9
2
0
Precisa de 7
7
1
Resposta: [0, 1]
Padrão descoberto: Para cada número, preciso do seu complemento.

"O que estou verificando repetidamente à mão?"

Para Two Sum: "O número necessário já existe?"

hash map busca rápida O(n) solution
03
Elabore uma Ideia de Solução
  • Generalize seu processo manual
  • Escolha a estrutura de dados certa
  • Verifique a complexidade de tempo
  • Verifique a complexidade de espaço
  • Preencha lacunas lógicas antes de programar

O Portão da Complexidade

Antes de escrever código, pergunte:

  • A força bruta é boa o suficiente?
  • O que as restrições permitem?
  • Funções auxiliares escondem custo extra?
  • Tenho certeza de que isso pode passar?

Nenhum código antes deste portão.

Passo 3: Bom vs Ruim

Ruim O(n²)

  • "Vou só programar e ver"
  • Sem discussão de complexidade
  • Lógica vaga
  • Estrutura de dados escolhida aleatoriamente

Bom O(n)

  • Apresenta a força bruta primeiro
  • Melhora intencionalmente
  • Escolhe estrutura baseada na necessidade
  • Explica por que se encaixa nas restrições

Two Sum: Bom Design

Força Bruta

Tente todos os pares

O(n²)

Melhor

Armazene números vistos em dict, verifique o complemento

O(n)

melhoria

Python: Ideia Central do Two Sum

def two_sum(nums, target): seen = {} for i, num in enumerate(nums): need = target - num if need in seen: return [seen[need], i] seen[num] = i
Curto
Legível
Estrutura corresponde à necessidade
Complexidade é clara
04
Ataque Sua Própria Ideia
"Faça o papel de advogado do diabo. Imagine que seu pior inimigo teve essa ideia. Você faria o seu melhor para destruí-la."
  • Faça o advogado do diabo com sua própria solução
  • Tente quebrá-la com entradas adversárias
  • Encontre suposições ocultas
  • Procure por casos extremos
  • Refute sua própria solução antes que o entrevistador o faça

Passo 4: Bom vs Ruim

Ruim

  • "Isso deveria funcionar"
  • Sem pensamento sobre casos extremos
  • Emocionalmente apegado à primeira ideia

Bom

  • Busca ativamente por falhas
  • Verifica suposições
  • Pergunta qual entrada quebraria a lógica
  • Faz o advogado do diabo contra a própria solução

Perguntas sobre Casos Extremos

Limites de Entrada

  • Entrada vazia
  • Elemento único
  • Tamanho máximo

Casos Extremos de Valor

  • Duplicatas
  • Negativos
  • Zeros

Verificação de Suposições

  • Ordenado vs desordenado
  • Único vs repetido
  • Overflow

Exemplo de uma Suposição Oculta

Suposição ruim: "A entrada está ordenada."

  • Isso foi garantido?

Se não, uma ideia de dois ponteiros pode falhar completamente.

Ordenado
1
2
3
4
5
Desordenado
3
1
4
2
5
05
Implemente
"Agora programe. Mas programe com disciplina:"
  • Escreva em blocos
  • Use nomes claros
  • Use funções auxiliares quando útil
  • Explique o que cada bloco faz

Passo 5: Bom vs Ruim

Ruim

  • Bloco gigante de código
  • Nomes ruins de variáveis
  • Sem estrutura
  • Difícil de explicar

Bom

  • Nomes limpos
  • Funções auxiliares onde necessário
  • Implementação bloco a bloco
  • Fácil de explicar

Implementação Ruim

def f(a, t): d = {} for i in range(len(a)): x = t - a[i] if x in d: return [d[x], i] d[a[i]] = i
f?
a?
t?
d?
x?

Nomes vagos → mais difícil de explicar → menor legibilidade → mais fácil de se confundir

Implementação Melhor

def two_sum(nums, target): value_to_index = {} for index, num in enumerate(nums): needed = target - num if needed in value_to_index: return [value_to_index[needed], index] value_to_index[num] = index
two_sum ✓
value_to_index ✓
needed ✓
index, num ✓

Legível → amigável ao entrevistador → mais fácil de depurar → mais fácil de narrar

Padrão de Função Auxiliar: Problemas de Grade

Dada uma grade 2D, visite vizinhos válidos para calcular ou atualizar um resultado.

def is_valid(row, col, rows, cols): return 0 <= row < rows and 0 <= col < cols
c

Padrão de Vetor de Direção

directions = [ (-1, 0), # up (1, 0), # down (0, -1), # left (0, 1), # right ]
c
  • Menos código repetido
  • Menos erros
  • Lógica de travessia mais limpa
06
Teste de Forma Abrangente
"Testar não é opcional. Testar é parte da solução."
  • Caso de exemplo
  • Caso extremo
  • Caso difícil
  • Caso de fronteira

Passo 6: Bom vs Ruim

Ruim - "Aleatório"

  • Testa apenas a entrada de exemplo
  • Tenta valores aleatórios
  • Para após o primeiro sucesso

Bom - "Intencional"

  • Cada teste tem um motivo
  • Mira nas suposições
  • Cobre fronteiras e duplicatas

Two Sum: Exemplos de Teste

[2, 7, 11, 15], target 9 caso básico
[3, 3], target 6 valores duplicados
[1, 2, 3, 4], target 7 resposta no final
[5, 4], target 9 entrada muito pequena
A lógica ainda funciona quando o mesmo valor aparece duas vezes?

O que Entrevistadores Percebem nos Testes

Quando você testa sem ser pedido, você sinaliza:

Disciplina
Responsabilidade
Maturidade
Mentalidade de Engenheiro
07
Simplifique e Limpe
"Depois que o código funcionar:"
  • Melhore os nomes
  • Remova a desordem
  • Simplifique a lógica
  • Adicione comentários mínimos se útil

Passo 7: Bom vs Ruim

Ruim

  • Deixa nomes confusos
  • Mantém código morto
  • Não faz revisão final

Bom

  • Remove a desordem
  • Melhora a clareza
  • Facilita a explicação final

Antes e Depois da Nomenclatura

Antes
mp = {}
Depois
value_to_index = {}

Clareza é parte da correção.

A Regra dos 3 Pontos

3
Qual é a ideia
Por que funciona
Por que é eficiente o suficiente

Se você precisa de 10 pontos, ainda não entendeu claramente.

Linha do Tempo de 40 Minutos da Entrevista

0-5 min
5-10
10-18
18-30
30-36
36-40
Entender
& reformular
Resolver
manualmente
Projetar &
complexidade
Implementar
Testar
Limpar
& resumir

A Regra do Pivô

Se você gasta 5-7 minutos e:

  • Não consegue justificar a complexidade
  • Continua falhando nos casos extremos
  • Não consegue explicar por que funciona
PIVOT
Nova estrutura de dados
Nova perspectiva
Versão mais simples primeiro
Peça uma dica focada

Pedido de Dica Bom vs Pedido de Dica Ruim

Bad
"Estou travado."
Melhor
"Tenho uma solução O(n²), mas acho que as restrições precisam de algo mais rápido. Devo pensar em hashing ou ordenação?"

Por que é melhor: mostra progresso, mostra consciência, faz uma pergunta direcionada.

Roteiro de Comunicação Reutilizável

  • "Deixe-me reformular o problema."
  • "Vou começar com um exemplo pequeno."
  • "A força bruta é O(n²), mas acho que podemos fazer melhor."
  • "Vou implementar isso em duas partes."
  • "Agora vou testar os casos extremos."
II
Passo a Passo Completo: Image Smoother
"Vamos aplicar todos os 7 passos a um segundo problema."

Problema: Image Smoother

Você recebe uma grade de inteiros 2D representando valores de pixels de uma imagem. Para cada célula, calcule o piso da média das células vizinhas válidas ao redor dela, incluindo a própria célula.
1
1
1
1
0
1
1
1
1

Célula central destacada em verde-azulado, vizinhos sombreados

Passo 1: Entenda - Image Smoother

  • Entrada: matriz de inteiros m × n
  • Saída: outra matriz m × n
  • Use apenas vizinhos válidos (ciente das fronteiras)
  • Inclua a própria célula central na média
  • A média usa divisão inteira (piso)
Canto: 3 vizinhos + própria = 4
c
n
×
n
n
×
×
×
×
Centro: 8 vizinhos + própria = 9
n
n
n
n
c
n
n
n
n

Passo 2: Resolução Manual - Image Smoother

Calcule o valor suavizado para a célula (0,0) à mão:

Grade de entrada
1
1
1
1
0
1
1
1
1
Cell (0,0) = 1
Vizinhos válidos:
(0,1) = 1
(1,0) = 1
(1,1) = 0
Sum = 1 + 1 + 1 + 0 = 3
Count = 4
floor(3/4) = 0
Padrão: para cada célula, reúna vizinhos válidos, some-os, divida pela contagem.

Passo 3: Projetar - Image Smoother

Decisões Chave

  • Use 9 offsets de direção (incluindo o centro)
  • Use verificação de limites is_valid
  • Aloque uma nova matriz de saída
  • Itere cada célula na grade

Complexidade

  • Cada célula verifica até 9 vizinhos
  • Total: O(m × n × 9) = O(m × n)
  • Space: O(m × n) for output
c

Passo 4: Atacar - Image Smoother

Perguntas de Fronteira

  • Cantos têm apenas 4 células (incluindo a própria)
  • Bordas têm apenas 6 células
  • E uma grade 1×1?

Perguntas de Valor

  • A célula central está incluída na média?
  • Divisão inteira (piso), não arredondamento
  • Todos os mesmos valores → inalterado?

Armadilhas de Implementação

  • Modificar a entrada no local (corrompe leituras posteriores)
  • Erro de um na verificação de limites
  • Divisão inteira vs ponto flutuante
Insight chave: precisamos de uma matriz de saída separada porque ler e escrever na mesma grade corrompe os resultados.

Passo 5: Implementar - Image Smoother (Configuração)

def image_smoother(img): rows, cols = len(img), len(img[0]) output = [[0] * cols for _ in range(rows)] neighbors = [ (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0), (1, 1), ]
-1,-1
-1,0
-1,1
0,-1
0,0
0,1
1,-1
1,0
1,1

Passo 5: Implementar - Solução Completa

def image_smoother(img): rows, cols = len(img), len(img[0]) output = [[0] * cols for _ in range(rows)] neighbors = [ (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0), (1, 1), ] for row in range(rows): for col in range(cols): total = 0 count = 0 for dr, dc in neighbors: nr, nc = row + dr, col + dc if 0 <= nr < rows and 0 <= nc < cols: total += img[nr][nc] count += 1 output[row][col] = total // count return output

Passo 6: Testar - Image Smoother

[[1,1,1],[1,0,1],[1,1,1]] caso de exemplo - célula central cercada
[[5,5,5],[5,5,5],[5,5,5]] todos os mesmos valores → saída inalterada
[[7]] 1×1 grid → output is [[7]]
[[1,1,1],[1,0,1],[1,1,1]], cell (0,0) célula de canto: (1+1+1+0)/4 = piso(3/4) = 0
Cada teste mira uma fronteira ou suposição específica.

Passo 7: Limpeza - Image Smoother

Antes da Limpeza

  • Nome de variável d para direções
  • Números mágicos como -1, 1 espalhados
  • Sem comentários na verificação de limites
  • Risco de mutação no local

Depois da Limpeza

  • neighbors é autodocumentado
  • Offsets de direção agrupados visualmente
  • Matriz de saída separada é explícita
  • O código lê como o algoritmo
Verificação final: Você consegue explicar cada linha em uma frase?

Image Smoother: Bom vs Ruim

Ruim

  • Começa a programar loops imediatamente
  • Esquece a alocação de saída
  • Esquece verificações de fronteira
  • Repete 9 condições manualmente

Bom

  • Identifica problema de vizinhos na grade
  • Usa offsets de vizinhos
  • Aloca saída primeiro
  • Trata limites centralmente

O que Candidatos Fortes Fazem

Desaceleram no início
Pensam antes de programar
Desafiam suas próprias ideias
Testam intencionalmente
Comunicam com clareza
Recuperam-se com calma

O que Candidatos Fracos Fazem

Correm para o código
Ignoram restrições
Esperam que a ideia funcione
Pulam casos extremos
Entram em pânico quando travados
Nunca limpam o código

Clareza primeiro.

Correção segundo.

Velocidade terceiro.

Velocidade sem clareza cria soluções erradas.

O problema não é o problema. O problema é a sua atitude em relação ao problema. Você entende?
- Capitão Jack Sparrow
1 / 56

Índice