Разбор задачи 3Sum на Python: от brute force до O(n²) с two pointers. Научись решать задачу с собеседований и избегать дубликатов. Попробуй прямо сейчас!
Задача 3Sum — одна из самых популярных на собеседованиях в FAANG. Она учит важному паттерну: зафиксировать один элемент и свести задачу к известной подзадаче. После фиксации одного числа остаётся 2Sum, который решается двумя указателями за O(n). Освоив 3Sum, ты автоматически поймёшь 4Sum и KSum.
Дан целочисленный массив nums. Нужно вернуть все уникальные тройки [nums[i], nums[j], nums[k]], такие что:
i != j, i != k, j != knums[i] + nums[j] + nums[k] = 0Пример:
Вход: nums = [-1, 0, 1, 2, -1, -4]
Выход: [[-1, -1, 2], [-1, 0, 1]]Самое очевидное решение — перебрать все возможные тройки тремя вложенными циклами. Для каждой тройки проверяем сумму, и если она равна нулю — добавляем в результат через множество для уникальности.
def three_sum_brute(nums):
n = len(nums)
result = set()
for i in range(n - 2):
for j in range(i + 1, n - 1):
for k in range(j + 1, n):
if nums[i] + nums[j] + nums[k] == 0:
# Сортируем тройку для уникальности
triplet = tuple(sorted([nums[i], nums[j], nums[k]]))
result.add(triplet)
return [list(t) for t in result]Сложность: O(n³) по времени, O(m) по памяти, где m — количество троек. На массиве из 1000 элементов это около 166 миллионов операций — слишком медленно для собеседования.
Ключевая идея: после сортировки массива задача сводится к 2Sum. Фиксируем один элемент nums[i], и ищем два других, сумма которых равна -nums[i]. Два указателя двигаются навстречу друг другу, что даёт O(n²).
nums[i]left = i + 1, right = n - 1left < right:left вправоright влевоdef three_sum(nums):
nums.sort() # O(n log n)
n = len(nums)
result = []
for i in range(n - 2):
# Пропускаем дубликаты фиксированного элемента
if i > 0 and nums[i] == nums[i - 1]:
continue
left = i + 1
right = n - 1
while left < right:
total = nums[i] + nums[left] + nums[right]
if total < 0:
left += 1 # Нужна большая сумма
elif total > 0:
right -= 1 # Нужна меньшая сумма
else:
# Нашли тройку
result.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
# Пропускаем дубликаты слева
while left < right and nums[left] == nums[left - 1]:
left += 1
# Пропускаем дубликаты справа
while left < right and nums[right] == nums[right + 1]:
right -= 1
return resultВозьмём массив [-4, -1, -1, 0, 1, 2]:
i = 1, nums[i] = -1. Нужна сумма b + c = 1left = 2 (указывает на -1), right = 5 (указывает на 2)left = 3 (0), right = 4 (1)left >= right, выходимРезультат: [[-1, -1, 2], [-1, 0, 1]]
Без пропуска дубликатов мы получим одинаковые тройки несколько раз. Например, для [-2, 0, 0, 0, 2, 2]:
[-2, 0, 2] появится 6 разПропуск дубликатов на всех трёх уровнях (фиксированный элемент, левый указатель, правый указатель) гарантирует уникальность.
Этот подход обобщается:
Когда видишь «тройки», «уникальные комбинации», «сумма равна цели» — сразу думай: сортировка → фиксация → два указателя.
Открой LeetCode, найди задачу 3Sum и реши её на Python, используя код из статьи. Затем попробуй 4Sum — паттерн тот же, только фиксируешь два элемента. Через час ты закрепишь навык, который пригодится на любом техническом собеседовании.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →