Pular para o conteudo principal

Duas somas

Como resolver um problema clássico de entrevista explicando o caminho, a troca envolvida e a escolha final.

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

Erros comuns

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
}
solution.ts
Travado? Dicas disponiveis.

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:

  1. Calcula o complemento: target - nums[i]
  2. Verifica se o complemento já está no Map
  3. Se estiver, você encontrou o par — retorna os dois índices
  4. Se não estiver, guarda o número atual no Map com 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, uso mapa hash para busca em O(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.

Proximo desafio Contém duplicata

Desafios relacionados

Artigos relacionados