20 de Março
Duas somas
Como resolver um problema clássico de entrevista explicando o caminho, a troca envolvida e a escolha final.
Arrays e Mapa Hash Iniciante 15 min TypeScript
Dado um array de inteiros nums e um inteiro target, retorne os índices de dois números cuja soma seja igual a target.
Você pode assumir que existe exatamente uma resposta válida e não pode usar o mesmo elemento duas vezes.
Exemplo:
Input: nums = [2, 7, 11, 15], target = 9
Output: [0, 1]
nums[0] + nums[1] = 2 + 7 = 9
O que perceber antes de codar
- Você retorna ÍNDICES (posições no array). A soma que você verifica é dos VALORES nessas posições. No exemplo, 2 + 7 = 9, então você retorna [0, 1] — as posições do 2 e do 7.
- Para cada número que você olha, existe exatamente um outro valor que completa a soma. Quanto é esse valor? Como você descobre?
- Você precisa saber onde cada número aparece no array — não só se ele existe.
Erros comuns
- ordenar cedo demais e esquecer que os indices originais importam
- guardar o número atual antes de checar o complemento e acabar reutilizando o mesmo indice
Passo 1 — Entender o que o problema pede
Antes de qualquer loop, leia o enunciado com calma.
O problema diz: "retorne os índices de dois números cuja soma seja igual a target."
Isso significa:
- Você procura dois valores no array que, somados, dão o target
- Você retorna as posições desses valores no array — não os valores em si
Exemplo concreto:
nums = [2, 7, 11, 15]
target = 9
2 + 7 = 9 → os valores são 2 e 7, nas posições 0 e 1 → retorne [0, 1]
Se você pensou "somar os índices", releia o exemplo. O que soma é o conteúdo do array, não as posições.
Passo 2 — Escrever a solução mais simples possível
A solução mais direta é: para cada número, compare com todos os outros.
Isso usa dois loops — um dentro do outro:
export function twoSum(nums: number[], target: number): [number, number] | null {
for (let i = 0; i < nums.length; i += 1) {
for (let j = i + 1; j < nums.length; j += 1) {
if (nums[i] + nums[j] === target) {
return [i, j]
}
}
}
return null
}
O loop externo pega cada posição i.
O loop interno verifica todas as posições j depois de i.
Se a soma bater com o target, retorna os dois índices.
Escreva isso no editor e rode os testes. Funciona. Mas é lento para arrays grandes porque você compara cada par possível.
Passo 3 — Entender por que dois loops é lento
Com n números, o loop interno roda aproximadamente n vezes para cada iteração do loop externo.
Resultado: o trabalho cresce com o quadrado do tamanho da entrada — O(n²).
Para 1000 números, você faz cerca de 500 mil comparações. Para 10000 números, cerca de 50 milhões.
O problema não é ter dois loops. O problema é que você está percorrendo o mesmo array de novo a cada iteração, repetindo trabalho que poderia ter sido guardado.
Passo 4 — Reformular a pergunta
No loop externo, você está em nums[i].
Você precisa saber se existe algum outro número no array que, somado a nums[i], dá target.
Esse número tem um valor exato. Você consegue calcular qual é:
complemento = target - nums[i]
Exemplo: target é 9, você está olhando para o 7. O complemento é 9 - 7 = 2.
A pergunta vira: o 2 já apareceu antes nesse array?
Se você soubesse isso instantaneamente — sem percorrer tudo de novo — não precisaria do segundo loop.
Tente modificar o código usando as dicas ao lado.
O editor interativo precisa de JavaScript. Voce ainda pode ler o desafio e copiar o codigo inicial abaixo.
Codigo inicial
export function twoSum(nums: number[], target: number): [number, number] | null {
// sua solução aqui
return null
}
Ainda não resolveu?
Ver a solução agora pode reduzir o aprendizado. Vale a pena tentar mais um pouco.
Abrir a solucao de referencia
Sem JavaScript, a solucao de referencia aparece inline em vez de um dialogo.
Solução
Complexidade final
Tempo: O(n)
Espaço: O(n)
Solução 1 — Força bruta O(n²)
Dois loops aninhados, compara cada par possível.
export function twoSum(nums: number[], target: number): [number, number] | null {
for (let i = 0; i < nums.length; i += 1) {
for (let j = i + 1; j < nums.length; j += 1) {
if (nums[i] + nums[j] === target) {
return [i, j]
}
}
}
return null
}
Funciona. Mas para n números, o loop interno roda n vezes para cada iteração do externo. O trabalho cresce com o quadrado — lento demais para entradas grandes.
Solução 2 — Mapa hash em O(n)
Passo 5 — Guardar o que já foi visto
A ideia é: ao invés de buscar o complemento no array, guarde os números que você já viu em uma estrutura separada.
Essa estrutura precisa responder em tempo constante: “esse número existe? E em qual posição ele estava?”
Em JavaScript, Map faz exatamente isso. Você guarda valor → índice:
const seen = new Map<number, number>()
seen.set(2, 0) // número 2 estava na posição 0
seen.has(2) // true — busca instantânea
seen.get(2) // 0 — posição do número 2
Passo 6 — Montar a solução final
Com Map, você percorre o array uma única vez:
- Calcula o complemento:
target - nums[i] - Verifica se o complemento já está no
Map - Se estiver, você encontrou o par — retorna os dois índices
- Se não estiver, guarda o número atual no
Mapcom sua posição
Um detalhe importante: cheque o complemento antes de guardar o número atual. Isso evita usar o mesmo elemento duas vezes.
export function twoSum(nums: number[], target: number): [number, number] | null {
const seen = new Map<number, number>()
for (let index = 0; index < nums.length; index += 1) {
const value = nums[index]
const complement = target - value
if (seen.has(complement)) {
return [seen.get(complement)!, index]
}
seen.set(value, index)
}
return null
}
Uma passada pelo array. Busca O(1) por complemento. Resultado: O(n) no total.
O que dizer na entrevista
Uma explicação curta e boa seria:
A versão inicial compara todos os pares e custa
O(n²). Como eu só preciso saber se o complemento já apareceu, usomapa hashpara busca emO(1)e resolvo em uma passada.
Isso mostra tres coisas:
- você entendeu a versão inicial e sabe nomear o custo dela
- você justificou a otimização com uma razão concreta
- você conectou a estrutura de dados a necessidade real
Quando esse padrão aparece de novo
A mesma ideia volta quando o problema pede:
- lembrar do que já apareceu
- detectar repetição
- descobrir se um complemento existe
- agrupar ou consultar rápido durante a iteração
O ponto não e decorar
mapa hash.
O ponto e perceber quando o problema pede memória durante a passada — e escolher a estrutura certa para isso.