0Pricing
DSA Interview Prep · Aula

Subarray Máximo e Subarray de Produto Máximo

Aplique o algoritmo de Kadane a maximum-sum-subarray e estenda-o para acompanhar os valores máximo e mínimo na variante de produto.

Subarray Máximo e Subarray de Produto Máximo é uma aula grátis de DSA Interview Prep no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DSA Interview Prep, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DSA Interview Prep inclui 4 aulas no total.

Problema do subvetor de soma máxima

O problema do subvetor de soma máxima solicita que você encontre o subvetor contíguo dentro de um vetor unidimensional de números que tenha a maior soma. Por exemplo, em [-2, 1, -3, 4, -1, 2, 1, -5, 4], o subvetor [4, -1, 2, 1] fornece a soma máxima de 6. Uma abordagem de força bruta O(n²) verifica todos os subvetores, mas o algoritmo de Kadane resolve o problema em O(n).

nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
# Brute force: O(n^2)
max_sum = float('-inf')
for i in range(len(nums)):
    curr = 0
    for j in range(i, len(nums)):
        curr += nums[j]
        max_sum = max(max_sum, curr)
print(max_sum)  # 6

Intuição do algoritmo de Kadane

O algoritmo de Kadane percorre o vetor uma vez, mantendo uma current_sum acumulada. Para cada elemento, você decide: é melhor estender o subvetor existente ou começar novamente a partir deste elemento? Se current_sum se torna negativa, isso apenas prejudicaria qualquer subvetor futuro, então reinicie. A recorrência é current_sum = max(num, current_sum + num).

def max_subarray(nums):
    max_sum = current_sum = nums[0]
    for num in nums[1:]:
        # Extend or start fresh?
        current_sum = max(num, current_sum + num)
        max_sum = max(max_sum, current_sum)
    return max_sum

nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
print(max_subarray(nums))  # 6

Acompanhando o algoritmo de Kadane

Vamos acompanhar o algoritmo de Kadane em [-2, 1, -3, 4, -1, 2, 1, -5, 4]: começamos com a soma atual igual a -2 e o máximo igual a -2. Em 1: soma atual=max(1,-2+1)=1, máximo=1. Em -3: soma atual=max(-3,1-3)=-2, máximo=1. Em 4: soma atual=max(4,-2+4)=4, máximo=4. Em -1: soma atual=3, máximo=4. Em 2: soma atual=5, máximo=5. Em 1: soma atual=6, máximo=6. Em -5: soma atual=1. Em 4: soma atual=5, máximo=6. O algoritmo identifica corretamente o subvetor que termina no índice 6 como o ideal.

def max_subarray_trace(nums):
    curr = max_sum = nums[0]
    for i, num in enumerate(nums[1:], 1):
        new_curr = max(num, curr + num)
        max_sum = max(max_sum, new_curr)
        print(f'i={i}, num={num}, curr: {curr}->{new_curr}, max={max_sum}')
        curr = new_curr
    return max_sum

max_subarray_trace([-2, 1, -3, 4, -1, 2, 1, -5, 4])

Retornando o subvetor propriamente dito

Se o entrevistador solicitar que você retorne o próprio subvetor (não apenas a soma), será necessário acompanhar os índices inicial e final. Ao reiniciar (porque num > current_sum + num), atualize um temp_start. Ao atualizar max_sum, salve temp_start como start e o índice atual como end. Isso acrescenta uma sobrecarga O(1) ao mesmo algoritmo O(n).

def max_subarray_indices(nums):
    max_sum = curr = nums[0]
    start = end = temp_start = 0
    for i in range(1, len(nums)):
        if nums[i] > curr + nums[i]:
            curr = nums[i]
            temp_start = i
        else:
            curr += nums[i]
        if curr > max_sum:
            max_sum = curr
            start, end = temp_start, i
    return max_sum, nums[start:end+1]

print(max_subarray_indices([-2, 1, -3, 4, -1, 2, 1, -5, 4]))
# (6, [4, -1, 2, 1])

Problema do subvetor de produto máximo

O problema do subvetor de produto máximo é mais complexo que a variante da soma por causa dos números negativos. Dois números negativos, quando multiplicados, resultam em um número positivo; assim, um produto muito negativo pode se tornar o máximo depois de ser multiplicado por outro número negativo. Para [2, 3, -2, 4], a resposta é 6 ([2, 3]). Para [-2, 0, -1], a resposta é 0. Precisamos acompanhar os produtos máximo e mínimo a cada etapa.

nums = [2, 3, -2, 4]
# [2,3,-2,4]: products [2, 6, -12, -48]
# subarrays: [2]=2, [2,3]=6, [3]=3, etc.
# max is 6 from subarray [2,3]

nums2 = [-2, 3, -4]
# [-2]*3*[-4] = 24
# negative*negative=positive!
print('Expected:', 24)

Acompanhando os produtos máximo e mínimo

A principal ideia é: em cada posição, o produto máximo atual é um de num, max_so_far * num ou min_so_far * num (este último ajuda quando um número negativo transforma o mínimo em máximo). O mesmo vale para o mínimo. Atualize ambos cur_max e cur_min simultaneamente usando os valores anteriores para evitar usar valores já atualizados na mesma etapa.

def max_product(nums):
    max_prod = min_prod = result = nums[0]
    for num in nums[1:]:
        # All three candidates for new max
        candidates = (num, max_prod * num, min_prod * num)
        max_prod, min_prod = max(candidates), min(candidates)
        result = max(result, max_prod)
    return result

print(max_product([2, 3, -2, 4]))    # 6
print(max_product([-2, 3, -4]))      # 24
print(max_product([-2, 0, -1]))      # 0
print(max_product([-2]))             # -2

Por que min_prod é importante

Considere [-3, -10, 5]. Depois de processar -3: máximo=-3, mínimo=-3. Depois de -10: os candidatos são (-10, 30, 30) → máximo=30, mínimo=-10. Depois de 5: os candidatos são (5, 150, -50) → máximo=150. Sem acompanhar min_prod, você perderia a inversão que ocorre quando um mínimo muito negativo é multiplicado por outro número negativo. Calcule sempre max e min a partir dos mesmos valores anteriores para evitar um erro de leitura de valor desatualizado.

def max_product_traced(nums):
    max_p = min_p = result = nums[0]
    for num in nums[1:]:
        prev_max, prev_min = max_p, min_p
        max_p = max(num, prev_max * num, prev_min * num)
        min_p = min(num, prev_max * num, prev_min * num)
        result = max(result, max_p)
        print(f'num={num}: max_p={max_p}, min_p={min_p}')
    return result

max_product_traced([-3, -10, 5])
# max_p after -10: 30 (flip!)
# max_p after 5: 150

Zeros reiniciam o produto

Um zero no vetor reinicia ambos os produtos acumulados para zero, dividindo efetivamente o vetor em subvetores independentes. Quando num = 0, tanto max_prod * 0 = 0 quanto min_prod * 0 = 0, portanto os três candidatos se tornam 0, e o resultado máximo anterior é preservado. Não é necessário nenhum código para casos especiais — a fórmula geral trata os zeros naturalmente.

def max_product(nums):
    max_p = min_p = result = nums[0]
    for num in nums[1:]:
        cands = (num, max_p * num, min_p * num)
        max_p, min_p = max(cands), min(cands)
        result = max(result, max_p)
    return result

# Zero splits array into independent subarrays
print(max_product([3, -1, 4, 0, 2, 5, -1]))   # 10 (2*5)
print(max_product([0, 2]))                       # 2
print(max_product([-1, 0, -2]))                  # 0

Alternativa: varredura do produto da esquerda para a direita

Uma abordagem alternativa percorre o vetor da esquerda para a direita e da direita para a esquerda, redefinindo o produto acumulado para 1 ao encontrar zero. O subvetor de produto máximo nunca atravessa um zero; portanto, se um número negativo piorar o resultado em uma direção, a varredura inversa capturará a inversão. Essa abordagem é elegante, mas o método de acompanhamento de mínimo/máximo é mais comumente esperado em entrevistas.

def max_product_sweep(nums):
    result = max(nums)
    left = right = 1
    n = len(nums)
    for i in range(n):
        left *= nums[i]
        right *= nums[n - 1 - i]
        result = max(result, left, right)
        if left == 0: left = 1
        if right == 0: right = 1
    return result

print(max_product_sweep([2, 3, -2, 4]))   # 6
print(max_product_sweep([-2, 3, -4]))     # 24
print(max_product_sweep([-2, 0, -1]))     # 0

Kadane versus produto: principais diferenças

Os subvetores de soma e de produto diferem de maneiras importantes. Para a soma, números negativos são sempre prejudiciais, então você reinicia de forma gananciosa. Para o produto, dois números negativos ajudam, portanto é necessário acompanhar os dois extremos. Além disso, os zeros encerram os produtos, mas são apenas levemente prejudiciais para as somas. Ao explicar a solução em entrevistas, reconheça explicitamente essas diferenças e explique por que é necessário acompanhar o mínimo antes de escrever qualquer código.

# Max Sum Subarray: O(n) time, O(1) space
def max_sum(nums):
    curr = result = nums[0]
    for n in nums[1:]:
        curr = max(n, curr + n)  # restart or extend
        result = max(result, curr)
    return result

# Max Product Subarray: O(n) time, O(1) space
def max_prod(nums):
    lo = hi = result = nums[0]
    for n in nums[1:]:
        lo, hi = min(n, lo*n, hi*n), max(n, lo*n, hi*n)
        result = max(result, hi)
    return result

print(max_sum([-2, 1, -3, 4, -1, 2, 1]))   # 6
print(max_prod([-2, 3, -4]))               # 24

Complexidade e dicas para entrevistas

Tanto o algoritmo de Kadane (soma máxima) quanto o acompanhamento de mínimo/máximo (produto máximo) são executados em tempo O(n) e usam espaço O(1). Dicas importantes para entrevistas: (1) Para a soma máxima, mencione a alternativa de Divisão e Conquista O(n log n) para demonstrar amplitude. (2) Para o produto máximo, enfatize que você atualiza min_prod e max_prod simultaneamente a partir dos valores anteriores para evitar usar dados desatualizados. (3) Sempre esclareça: o vetor pode estar vazio? O subvetor precisa ser não vazio? (Sim, por convenção, ele precisa ser não vazio.)

# Both run O(n) time, O(1) space
# Kadane handles: all negative (returns least negative)
# Product handles: zeros (resets naturally), negatives (tracks both extremes)

nums_all_neg = [-5, -2, -8]
print('Max sum (all neg):', max(max(nums_all_neg[0:1]),
      max(x for x in nums_all_neg)))  # -2
# Correct: return the maximum element when all are negative

Verificação rápida

Teste sua compreensão dos conceitos de Estruturas de Dados e Algoritmos — Preparação para Entrevistas de Programação desta lição.

Recapitulação da lição

Nesta lição, você aprendeu: o algoritmo de Kadane resolve o subvetor de soma máxima em O(n), escolhendo estendê-lo ou reiniciá-lo em cada elemento, o subvetor de produto máximo exige acompanhar os produtos acumulados mínimo e máximo devido às inversões causadas por números negativos e os zeros reiniciam naturalmente o produto acumulado, sem código para casos especiais. Em seguida, exploraremos o problema de segmentação de palavras usando uma tabela DP unidimensional.

Perguntas Frequentes

A aula “Subarray Máximo e Subarray de Produto Máximo” é grátis?

Sim — o texto completo de “Subarray Máximo e Subarray de Produto Máximo” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DSA Interview Prep, atualize para CoddyKit PRO. O curso de DSA Interview Prep inclui 4 aulas no total.

O que vou aprender em “Subarray Máximo e Subarray de Produto Máximo”?

Aplique o algoritmo de Kadane a maximum-sum-subarray e estenda-o para acompanhar os valores máximo e mínimo na variante de produto. Você pratica DSA Interview Prep com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar DSA Interview Prep?

Nenhuma experiência prévia é necessária. DSA Interview Prep no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “Subarray Máximo e Subarray de Produto Máximo”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de DSA Interview Prep?

Sim. Cada aula de DSA Interview Prep inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. House Robber: Recorrência Escolher ou Ignorar
  2. Subarray Máximo e Subarray de Produto Máximo
  3. Quebra de Palavras e Segmentação de Strings
  4. Decodificando Formas e Contando Caminhos
← Voltar para DSA Interview Prep