Pular para o conteudo principal

Produto do array exceto ele mesmo

Como usar prefixo e sufixo para montar a resposta local de cada índice a partir do contexto dos lados.

Dado um array de inteiros nums, retorne um array answer tal que answer[i] seja igual ao produto de todos os elementos de nums exceto nums[i].

Você deve fazer isso sem usar divisao.

Exemplo:

Input:  nums = [1,2,3,4]
Output: [24,12,8,6]

O que perceber antes de codar

Erros comuns

Passo 1 — Enxergar o caminho proibido

Sem a restrição, a ideia seria:

  • calcular o produto total
  • dividir por nums[i]

O problema proibe isso.

E faz sentido. Zero complica tudo, e o exercicio quer outro raciocínio.

Passo 2 — Perguntar de onde vem a resposta de cada indice

Para answer[i], você precisa do produto de:

  • tudo que esta antes de i
  • tudo que esta depois de i

Isso sugere quebrar a informação em duas partes.

Passo 3 — Construir prefixos

Se answer[i] começar guardando o produto da esquerda:

  • answer[0] = 1
  • answer[1] = nums[0]
  • answer[2] = nums[0] * nums[1]

entao metade da resposta já esta pronta.

Passo 4 — Multiplicar pelos sufixos

Depois faca a passada da direita para a esquerda:

  • mantenha um produto acumulado da direita
  • multiplique esse valor em answer[i]

Assim cada posição vira:

prefixoDaEsquerda * sufixoDaDireita

O editor interativo precisa de JavaScript. Voce ainda pode ler o desafio e copiar o codigo inicial abaixo.

Codigo inicial

export function productExceptSelf(nums: number[]): number[] {
  // sua solução aqui
  return []
}
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(1)

Solução 1 — Recalcular para cada indice

Correta, mas lenta demais.

export function productExceptSelf(nums: number[]): number[] {
  const answer: number[] = []

  for (let index = 0; index < nums.length; index += 1) {
    let product = 1

    for (let other = 0; other < nums.length; other += 1) {
      if (other !== index) {
        product *= nums[other]
      }
    }

    answer.push(product)
  }

  return answer
}

Solução 2 — Acumuladores de prefixo + sufixo em O(n)

Agora cada passada acumula metade da informação.

export function productExceptSelf(nums: number[]): number[] {
  const answer = new Array(nums.length).fill(1)

  let prefix = 1
  for (let index = 0; index < nums.length; index += 1) {
    answer[index] = prefix
    prefix *= nums[index]
  }

  let suffix = 1
  for (let index = nums.length - 1; index >= 0; index -= 1) {
    answer[index] *= suffix
    suffix *= nums[index]
  }

  return answer
}

Cada indice recebe o produto da esquerda numa passada e o da direita na outra.

O que dizer na entrevista

Uma explicação curta e boa seria:

Sem divisao, cada resposta precisa ser pensada como produto da esquerda vezes produto da direita. Eu faco uma passada guardando prefixos no array de resposta e outra passada multiplicando pelos sufixos acumulados. Isso mantem O(n) sem precisar recalcular tudo para cada indice.

Isso mostra que você:

  • entendeu por que a divisao foi proibida
  • quebrou o problema em partes reutilizaveis
  • explicou prefixo e sufixo como composição, não como truque

Quando esse padrão aparece de novo

Esse padrão reaparece quando você precisa:

  • combinar informação acumulada dos dois lados
  • responder por indice sem recalcular tudo
  • trabalhar com prefix sum ou produto de prefixo
  • transformar dependência global em duas passadas locais

O ponto não e decorar productExceptSelf.

O ponto e perceber quando a resposta pode ser montada por acumulados parciais.

Proximo desafio Mesclar intervalos Desafio anterior Agrupar anagramas

Desafios relacionados

Artigos relacionados