Продукт массива кроме себя
Условие
Дан массив чисел. Верните массив, где каждый элемент — это произведение всех остальных элементов исходного массива.
Ограничения: нельзя использовать деление, O(n) по времени.
Пример
product_except_self([1, 2, 3, 4]) → [24, 12, 8, 6] product_except_self([-1, 1, 0, -3, 3]) → [0, 0, 9, 0, 0]
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Продукт массива кроме себя
Это классическая задача на префиксы и суффиксы. Ключевая идея: для каждой позиции i результат = произведение всех элементов слева × произведение всех элементов справа.
Подход: Два прохода
Вместо того чтобы считать произведение всех элементов и делить (что запрещено), мы:
- Первый проход слева направо — считаем произведение всех элементов слева
- Второй проход справа налево — умножаем на произведение элементов справа
Визуализация
Массив: [1, 2, 3, 4]
Шаг 1: Произведение слева
Для индекса i = произведение [0...i-1]
position: 0 1 2 3
product: 1 1 1*2 1*2*3
left: [ 1, 1, 2, 6 ]
Шаг 2: Произведение справа
Для индекса i = произведение [i+1...n-1]
right: [ 24, 12, 4, 1 ]
Шаг 3: Результат = левое[i] * правое[i]
result: [1*24, 1*12, 2*4, 6*1]
result: [24, 12, 8, 6]
Реализация (O(n) время, O(1) доп. память)
def product_except_self(nums):
"""
Для каждого элемента вычисляет произведение всех остальных.
Сложность: O(n) время, O(1) доп. память (не считая результата)
Метод: Префикс и суффикс
"""
n = len(nums)
result = [1] * n
# Шаг 1: Считаем произведение всех элементов слева
prefix = 1
for i in range(n):
result[i] = prefix
prefix *= nums[i]
# Шаг 2: Считаем произведение элементов справа и умножаем
suffix = 1
for i in range(n - 1, -1, -1):
result[i] *= suffix
suffix *= nums[i]
return result
Пошаговое выполнение
Входные данные: [1, 2, 3, 4]
Шаг 1: Слева направо (prefix)
i=0: result[0] = 1 (prefix=1), prefix = 1*1 = 1
i=1: result[1] = 1 (prefix=1), prefix = 1*2 = 2
i=2: result[2] = 2 (prefix=2), prefix = 2*3 = 6
i=3: result[3] = 6 (prefix=6), prefix = 6*4 = 24
result = [1, 1, 2, 6]
Шаг 2: Справа налево (suffix)
i=3: result[3] *= 1 (suffix=1) → 6*1=6, suffix = 1*4 = 4
i=2: result[2] *= 4 (suffix=4) → 2*4=8, suffix = 4*3 = 12
i=1: result[1] *= 12 (suffix=12) → 1*12=12, suffix = 12*2 = 24
i=0: result[0] *= 24 (suffix=24) → 1*24=24, suffix = 24*1 = 24
result = [24, 12, 8, 6]
Результат: [24, 12, 8, 6] ✓
С нулями и отрицательными числами
def product_except_self_detailed(nums):
n = len(nums)
result = [1] * n
# Левые произведения
prefix = 1
for i in range(n):
result[i] = prefix
prefix *= nums[i]
# Правые произведения
suffix = 1
for i in range(n - 1, -1, -1):
result[i] *= suffix
suffix *= nums[i]
return result
# Примеры
print(product_except_self_detailed([1, 2, 3, 4])) # [24, 12, 8, 6]
print(product_except_self_detailed([2, 3, 4, 5])) # [60, 40, 30, 24]
print(product_except_self_detailed([-1, 1, 0, -3, 3])) # [0, 0, 9, 0, 0]
Для [-1, 1, 0, -3, 3]:
Шаг 1 (prefix слева):
result = [1, -1, -1, 0, 0] (произведение всего слева)
Шаг 2 (suffix справа):
result[4] = 0 * 1 = 0 ← есть 0 слева
result[3] = 0 * (-3) = 0 ← есть 0 слева
result[2] = (-1)*(-3)*3 = 9 ← нет нуля в других элементах
result[1] = (-1) * (-3*3) = (-1)*(-9) = 9... нет, не правильно
Актуально:
result[2] = (-1)*1 умножить на (3*(-3)) = (-1)*(-9) = 9
Итоговый результат: [0, 0, 9, 0, 0]
Наивный подход (с делением — запрещён)
def product_except_self_naive(nums):
"""
❌ Этот подход ЗАПРЕЩЁН в задаче из-за требования без деления
"""
total_product = 1
zero_count = 0
zero_index = -1
for i, num in enumerate(nums):
if num == 0:
zero_count += 1
zero_index = i
else:
total_product *= num
result = [0] * len(nums)
if zero_count > 1:
return result # Больше одного нуля → все нули
if zero_count == 1:
result[zero_index] = total_product
return result
# Нет нулей
for i in range(len(nums)):
result[i] = total_product // nums[i] # ← Деление запрещено!
return result
Оптимизация памяти
Технически, в нашем решении мы используем O(n) доп. памяти (для левых произведений), но это можно оптимизировать, используя результирующий массив для хранения префиксов:
def product_except_self_optimized(nums):
n = len(nums)
result = [1] * n
# Префиксы в result
for i in range(1, n):
result[i] = result[i - 1] * nums[i - 1]
# Суффиксы и финальное умножение
suffix = 1
for i in range(n - 1, -1, -1):
result[i] *= suffix
suffix *= nums[i]
return result
Это работает в O(n) времени и O(1) дополнительной памяти (если не считать результирующий массив).
Тестирование
def test_product_except_self():
# Базовые случаи
assert product_except_self([1, 2, 3, 4]) == [24, 12, 8, 6]
assert product_except_self([2, 3, 4, 5]) == [60, 40, 30, 24]
# С нулями
assert product_except_self([0, 0]) == [0, 0]
assert product_except_self([1, 0, 3, 4]) == [0, 12, 0, 0]
# С отрицательными
assert product_except_self([-1, 1, 0, -3, 3]) == [0, 0, 9, 0, 0]
assert product_except_self([-1, -2, -3]) == [-6, -3, -2]
# Один элемент (не стандартный случай, но проверим)
assert product_except_self([5]) == [1]
print("Все тесты пройдены!")
test_product_except_self()
Ключевая идея этой задачи: нужно думать о массиве как о произведении левой и правой частей, а не как о целом произведении.