0Pricing
Coding Interview Prep · Lección

Subarray máximo y subarray de producto máximo

Aplique el algoritmo de Kadane a maximum-sum-subarray y amplíelo para realizar un seguimiento de los valores máximo y mínimo en la variante de producto.

Subarray máximo y subarray de producto máximo es una lección gratuita de Coding Interview Prep en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Coding Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Coding Interview Prep incluye 4 lecciones en total.

Problema del subarreglo de suma máxima

El problema Maximum Subarray consiste en encontrar el subarreglo contiguo dentro de un arreglo unidimensional de números que tenga la suma más grande. Por ejemplo, en [-2, 1, -3, 4, -1, 2, 1, -5, 4], el subarreglo [4, -1, 2, 1] produce la suma máxima de 6. Un enfoque de fuerza bruta O(n²) comprueba todos los subarreglos, pero el algoritmo de Kadane lo resuelve en 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

Intuición del algoritmo de Kadane

El algoritmo de Kadane recorre el arreglo una sola vez y mantiene un current_sum acumulado. En cada elemento, debe decidir: ¿es mejor extender el subarreglo existente o comenzar de nuevo desde este elemento? Si current_sum se vuelve negativo, solo perjudicaría a cualquier subarreglo futuro, por lo que se reinicia. La recurrencia es 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

Recorrido del algoritmo de Kadane

Recorramos el algoritmo de Kadane sobre [-2, 1, -3, 4, -1, 2, 1, -5, 4]: comenzamos con curr=-2, max=-2. En 1: curr=max(1,-2+1)=1, max=1. En -3: curr=max(-3,1-3)=-2, max=1. En 4: curr=max(4,-2+4)=4, max=4. En -1: curr=3, max=4. En 2: curr=5, max=5. En 1: curr=6, max=6. En -5: curr=1. En 4: curr=5, max=6. El algoritmo identifica correctamente el subarreglo que termina en el índice 6 como el óptimo.

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])

Devolver el subarreglo real

Si en la entrevista le piden devolver el subarreglo completo (no solo la suma), debe realizar un seguimiento de los índices inicial y final. Cuando reinicie (porque num > current_sum + num), actualice un temp_start. Cuando actualice max_sum, guarde temp_start como start y el índice actual como end. Esto añade un coste adicional de O(1) al mismo algoritmo de 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 del subarreglo de producto máximo

El problema Maximum Product Subarray es más complicado que la variante de suma debido a los números negativos. Dos números negativos producen un resultado positivo, por lo que un producto muy negativo puede convertirse en el máximo al multiplicarse por otro negativo. Para [2, 3, -2, 4], la respuesta es 6 ([2, 3]). Para [-2, 0, -1], la respuesta es 0. Debemos realizar un seguimiento de los productos máximo y mínimo en cada paso.

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)

Seguimiento de los productos máximo y mínimo

La idea clave es la siguiente: en cada posición, el producto máximo actual es uno de estos valores: num, max_so_far * num o min_so_far * num (este último resulta útil cuando un número negativo convierte el mínimo en máximo). De forma similar, se calcula el mínimo. Actualice ambos valores, cur_max y cur_min, simultáneamente usando los valores anteriores para evitar utilizar valores ya actualizados en el mismo paso.

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 qué importa min_prod

Considere [-3, -10, 5]. Después de procesar -3: max=-3, min=-3. Después de -10: los candidatos son (-10, 30, 30) → max=30, min=-10. Después de 5: los candidatos son (5, 150, -50) → max=150. Sin realizar un seguimiento de min_prod, no detectaría el cambio que ocurre cuando un mínimo muy negativo se multiplica por otro número negativo. Calcule siempre max y min a partir de los mismos valores anteriores para evitar un error de lectura de valores obsoletos.

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

Los ceros reinician el producto

Un cero en el arreglo reinicia ambos productos acumulados a cero, lo que divide eficazmente el arreglo en subarreglos independientes. Cuando num = 0, tanto max_prod * 0 = 0 como min_prod * 0 = 0, por lo que los tres candidatos se convierten en 0 y se conserva el máximo del resultado anterior. No se necesita código para casos especiales: la fórmula general gestiona los ceros de forma natural.

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: recorrido del producto de izquierda a derecha y de derecha a izquierda

Un enfoque alternativo recorre el arreglo de izquierda a derecha y de derecha a izquierda, reiniciando el producto acumulado a 1 cuando encuentra un cero. El subarreglo de producto máximo nunca atraviesa un cero, por lo que, si un número negativo produce un resultado desfavorable en una dirección, el recorrido inverso detectará el cambio. Este enfoque es elegante, pero en las entrevistas suele esperarse más el método de seguimiento de mínimos y máximos.

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 frente al producto: diferencias clave

Los subarreglos de suma y de producto se diferencian en aspectos importantes. En la suma, los negativos siempre son perjudiciales, por lo que debe reiniciar de forma voraz. En el producto, dos negativos pueden ser beneficiosos, así que debe realizar un seguimiento de ambos extremos. Además, los ceros detienen los productos, mientras que solo son ligeramente perjudiciales para las sumas. Al explicarlo en una entrevista, reconozca explícitamente estas diferencias y explique por qué es necesario realizar un seguimiento del mínimo antes de escribir 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

Complejidad y consejos para entrevistas

Tanto el algoritmo de Kadane (suma máxima) como el seguimiento de mínimos y máximos (producto máximo) se ejecutan en tiempo O(n) y utilizan espacio O(1). Consejos clave para la entrevista: (1) Para la suma máxima, mencione la alternativa de divide y vencerás O(n log n) para demostrar amplitud de conocimientos. (2) Para el producto máximo, destaque que debe actualizar min_prod y max_prod simultáneamente a partir de los valores anteriores para evitar usar datos obsoletos. (3) Aclare siempre lo siguiente: ¿puede estar vacío el arreglo? ¿Debe el subarreglo ser no vacío? (Sí, por convención debe ser no vacío).

# 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

Comprobación rápida

Ponga a prueba su comprensión de los conceptos de Data Structures & Algorithms — Coding Interview Prep de esta lección.

Resumen de la lección

En esta lección aprendió: el algoritmo de Kadane resuelve el subarreglo de suma máxima en O(n) eligiendo extenderlo o reiniciarlo en cada elemento, el subarreglo de producto máximo requiere realizar un seguimiento de los productos acumulados mínimo y máximo debido a los cambios de signo de los números negativos y los ceros reinician de forma natural el producto acumulado sin necesidad de código para casos especiales. A continuación, exploraremos el problema Word Break mediante una tabla de DP unidimensional.

Preguntas frecuentes

¿La lección «Subarray máximo y subarray de producto máximo» es gratis?

Sí — el texto completo de «Subarray máximo y subarray de producto máximo» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Coding Interview Prep, actualiza a CoddyKit PRO. El curso de Coding Interview Prep incluye 4 lecciones en total.

¿Qué aprenderé en «Subarray máximo y subarray de producto máximo»?

Aplique el algoritmo de Kadane a maximum-sum-subarray y amplíelo para realizar un seguimiento de los valores máximo y mínimo en la variante de producto. Practicas Coding Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Coding Interview Prep?

No se requiere experiencia previa. Coding Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Subarray máximo y subarray de producto máximo»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Coding Interview Prep?

Sí. Cada lección de Coding Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. House Robber: recurrencia de tomar u omitir
  2. Subarray máximo y subarray de producto máximo
  3. Word Break y segmentación de strings
  4. Decode Ways y conteo de rutas
← Volver a Coding Interview Prep