Эффективная работа с массивами и строками с помощью двух указателей. Задачи на сумму, слияние, удаление дубликатов.
Один из самых элегантных паттернов для работы с массивами и строками: два указателя движутся навстречу или в одном направлении, сокращая сложность с O(n²) до O(n)
Two Pointers — это техника использования двух индексов (указателей), которые движутся по массиву или строке определённым образом для решения задачи за один проход.
Алгоритм — точная последовательность шагов для решения задачи.
Структура данных — способ организации и хранения данных для эффективного доступа и изменения.
Массив — упорядоченная коллекция элементов одного типа.
Подмассив — непрерывная часть массива (элементы идут подряд).
Подпоследовательность — элементы массива в том же порядке, но не обязательно непрерывные.
Подстрока — непрерывная часть строки.
Префикс — начало строки (подстрока, начинающаяся с первого символа).
Суффикс — конец строки (подстрока, заканчивающаяся последним символом).
Временная сложность — оценка времени выполнения алгоритма в зависимости от размера входных данных.
Пространственная сложность — оценка объёма памяти, используемого алгоритмом.
O-нотация (Big O) — способ описания верхней границы сложности алгоритма.
Хэш-функция — функция, преобразующая входные данные в число (хэш).
Хэш-таблица — структура данных для быстрого поиска по ключу (в среднем O(1)).
Коллизия — ситуация, когда два разных ключа дают одинаковый хэш.
| Тип | Описание | Пример |
|---|---|---|
| Навстречу | left от начала, right от конца, движутся к центру | Two Sum в отсортированном массиве |
| В одном направлении | Оба движутся слева направо, fast опережает slow | Удаление дубликатов |
| Разные скорости | slow += 1, fast += 2 | Поиск середины (Floyd's Algorithm) |
def two_pointers_opposite(arr, target):
left, right = 0, len(arr) - 1
while left < right:
current = arr[left] + arr[right]
if current == target:
return [left, right]
elif current < target:
left += 1 # Нужно больше
else:
right -= 1 # Нужно меньше
return [-1, -1]left = 0, right = n - 1left (движемся к большим значениям)right (движемся к меньшим значениям)Почему это работает? В отсортированном массиве движение left вправо увеличивает сумму, движение right влево — уменьшает.
def two_pointers_same_direction(arr):
slow = 0
for fast in range(len(arr)):
# Условие для продвижения slow
if some_condition(arr[fast]):
arr[slow] = arr[fast]
slow += 1
return slow # Новая длинаdef remove_duplicates(nums):
"""Удалить дубликаты из отсортированного массива in-place"""
if not nums:
return 0
slow = 0
for fast in range(1, len(nums)):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
return slow + 1 # Новая длинаУсловие:
Дан отсортированный по неубыванию массив целых чисел numbers и целевое значение target. Необходимо найти два числа в массиве, сумма которых равна target. Верните индексы этих чисел (в 1-based формате, т.е. первый индекс = 1, второй индекс = 2).
Гарантируется, что решение существует и каждый элемент можно использовать только один раз.
Примеры:
Ввод: numbers = [2, 7, 11, 15], target = 9 Вывод: [1, 2] Объяснение: numbers[0] + numbers[1] = 2 + 7 = 9, возвращаем [1, 2] (1-based индексы)
Ввод: numbers = [2, 3, 4], target = 6 Вывод: [1, 3]
Ввод: numbers = [-1, 0], target = -1 Вывод: [1, 2]
Ограничения:
💡 Подсказка: Поскольку массив отсортирован, можно использовать два указателя: один в начале, другой в конце. Если сумма меньше target — двигаем левый указатель вправо (увеличиваем сумму), если больше — двигаем правый влево (уменьшаем сумму).
Массив: [2, 7, 11, 15], target = 9
Шаг 1: Шаг 2:
┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐
│ 2 │ 7 │ 11│ 15│ │ 2 │ 7 │ 11│ 15│
└───┴───┴───┴───┘ └───┴───┴───┴───┘
↑ ↑ ↑ ↑
left=0 right=3 left=0 right=2
sum = 17 > 9 → sum = 13 > 9 →
right-- right--
Шаг 3: Шаг 4 (НАЙДЕНО):
┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐
│ 2 │ 7 │ 11│ 15│ │ 2 │ 7 │ 11│ 15│
└───┴───┴───┴───┘ └───┴───┴───┴───┘
↑ ↑ ↑ ↑
left=0 right=1 left=0 right=1
sum = 9 == 9 ✓ return [1, 2] (1-based)
Решение:
def twoSum(numbers, target):
left, right = 0, len(numbers) - 1
while left < right:
current_sum = numbers[left] + numbers[right]
if current_sum == target:
return [left + 1, right + 1] # 1-based индексы
elif current_sum < target:
left += 1
else:
right -= 1
return [-1, -1]Объяснение:
left вправо (увеличиваем сумму)right влево (уменьшаем сумму)Сложность:
Условие:
Дана строка s. Проверить, является ли она палиндромом, учитывая только буквенно-цифровые символы (буквы и цифры) и игнорируя регистр символов.
Палиндром — это строка, которая читается одинаково слева направо и справа налево.
Примеры:
Ввод: s = "A man, a plan, a canal: Panama" Вывод: true Объяснение: "amanaplanacanalpanama" является палиндромом
Ввод: s = "race a car" Вывод: false Объяснение: "raceacar" не является палиндромом
Ввод: s = " " Вывод: true Объяснение: После удаления неалфавитных символов остаётся пустая строка, которая считается палиндромом
Ограничения:
💡 Подсказка: Используйте два указателя: один в начале строки, другой в конце. Пропускайте неалфавитные символы и сравнивайте символы в нижнем регистре. Если все совпадут — это палиндром.
Решение:
def isPalindrome(s):
left, right = 0, len(s) - 1
while left < right:
# Пропускаем неалфавитные символы слева
while left < right and not s[left].isalnum():
left += 1
# Пропускаем неалфавитные символы справа
while left < right and not s[right].isalnum():
right -= 1
# Сравниваем символы (игнорируя регистр)
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return TrueОбъяснение:
Сложность:
Условие:
Дан массив целых чисел height длины n, где каждый элемент представляет высоту вертикальной линии на координате i (т.е. линия проходит от (i, 0) до (i, height[i])).
Найдите две линии, которые вместе с осью x образуют контейнер, вмещающий максимальное количество воды.
Верните максимальную площадь воды, которую может вместить контейнер.
Примеры:
Ввод: height = [1, 8, 6, 2, 5, 4, 8, 3, 7] Вывод: 49 Объяснение: Максимальная площадь достигается между линиями на индексах 1 и 8: ширина = 7, высота = min(8, 7) = 7, площадь = 7 × 7 = 49
Ввод: height = [1, 1] Вывод: 1
Ограничения:
💡 Подсказка: Начните с двух указателей на краях массива (максимальная ширина). Площадь = ширина × min(высота1, высота2). Двигайте указатель с меньшей высотой — только так можно потенциально увеличить площадь.
height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
Шаг 1 (максимальная ширина):
8 │ │
7 │ │ │
6 │ │ │
5 │ │ │ │ │
4 │ │ │ │ │ │
3 │ │ │ │ │ │ │ │
2 │ │ │ │ │ │ │ │ │
1 │ │ │ │ │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┴───┘
↑ ↑
left=0 right=8
width = 8, h = min(1,7) = 1
area = 8 × 1 = 8
Шаг 2 (двинули left, т.к. height[left] < height[right]):
8 │ │
7 │ │ │
6 │ │ │
5 │ │ │ │ │
4 │ │ │ │ │ │
3 │ │ │ │ │ │ │ │
2 │ │ │ │ │ │ │ │ │
1 │ │ │ │ │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┴───┘
↑ ↑
left=1 right=8
width = 7, h = min(8,7) = 7
area = 7 × 7 = 49 ← МАКСИМУМ!
Решение:
def maxArea(height):
left, right = 0, len(height) - 1
max_area = 0
while left < right:
# Площадь = ширина × минимальная высота
width = right - left
h = min(height[left], height[right])
area = width * h
max_area = max(max_area, area)
# Двигаем указатель с меньшей высотой
if height[left] < height[right]:
left += 1
else:
right -= 1
return max_areaОбъяснение:
Почему это жадный алгоритм работает?
Сложность:
Условие:
Дан массив целых чисел nums длины n. Найдите все уникальные тройки [nums[i], nums[j], nums[k]], такие что:
Верните массив всех уникальных троек. Решение не должно содержать дублирующихся троек.
Примеры:
Ввод: nums = [-1, 0, 1, 2, -1, -4] Вывод: [[-1, -1, 2], [-1, 0, 1]] Объяснение:
Ввод: nums = [0, 1, 1] Вывод: []
Ввод: nums = [0, 0, 0] Вывод: [[0, 0, 0]]
Ограничения:
💡 Подсказка: Отсортируйте массив. Зафиксируйте первый элемент, затем используйте два указателя для поиска пары с суммой, равной -nums[i]. Пропускайте дубликаты на всех уровнях.
Решение:
def threeSum(nums):
nums.sort()
result = []
n = len(nums)
for i in range(n - 2):
# Пропускаем дубликаты для первого числа
if i > 0 and nums[i] == nums[i - 1]:
continue
# Two Pointers для оставшихся двух чисел
left, right = i + 1, n - 1
while left < right:
current_sum = nums[i] + nums[left] + nums[right]
if current_sum == 0:
result.append([nums[i], nums[left], nums[right]])
# Пропускаем дубликаты
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
elif current_sum < 0:
left += 1
else:
right -= 1
return resultОбъяснение:
nums[i]-nums[i]Сложность:
Условие:
Дан отсортированный по неубыванию массив целых чисел nums. Удалите дубликаты элементов in-place так, чтобы каждый уникальный элемент встречался только один раз. Относительный порядок элементов должен быть сохранён.
Верните новую длину массива после удаления дубликатов.
Примеры:
Ввод: nums = [1, 1, 2] Вывод: 2, nums = [1, 2, _] Объяснение: Первые два элемента равны 1 и 2 соответственно. Остальные элементы могут быть любыми.
Ввод: nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4] Вывод: 5, nums = [0, 1, 2, 3, 4, _, _, _, _, _]
Ограничения:
💡 Подсказка: Используйте два указателя:
slowуказывает на последний уникальный элемент,fastсканирует массив. Когда находим новый уникальный элемент — копируем его на позициюslow + 1.
Решение:
def removeDuplicates(nums):
if not nums:
return 0
slow = 0
for fast in range(1, len(nums)):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
return slow + 1Объяснение:
slow указывает на последний уникальный элементfast сканирует массивslow + 1Визуализация:
[1, 1, 2, 2, 3]
↑ ↑
slow fast
[1, 2, 2, 2, 3]
↑ ↑
slow fast
[1, 2, 3, 2, 3]
↑ ↑
slow fast
Сложность:
Условие:
Даны два отсортированных по неубыванию массива целых чисел nums1 и nums2 длины m и n соответственно.
Слейте nums2 в nums1 так, чтобы nums1 содержал все элементы в отсортированном порядке.
Массив nums1 имеет длину m + n, где первые m элементов содержат значения для слияния, а последние n элементов равны 0 и должны быть проигнорированы.
Примеры:
Ввод: nums1 = [1, 2, 3, 0, 0, 0], m = 3, nums2 = [2, 5, 6], n = 3 Вывод: [1, 2, 2, 3, 5, 6] Объяснение: Массивы [1, 2, 3] и [2, 5, 6] слиты в [1, 2, 2, 3, 5, 6]
Ввод: nums1 = [1], m = 1, nums2 = [], n = 0 Вывод: [1]
Ограничения:
💡 Подсказка: Записывайте элементы с конца массива nums1, чтобы не затереть ещё не обработанные элементы. Сравнивайте текущие элементы обоих массивов и записывайте больший.
Решение:
def merge(nums1, m, nums2, n):
# Три указателя: с конца обоих массивов и позиции записи
i, j, k = m - 1, n - 1, m + n - 1
while j >= 0:
if i >= 0 and nums1[i] > nums2[j]:
nums1[k] = nums1[i]
i -= 1
else:
nums1[k] = nums2[j]
j -= 1
k -= 1Объяснение:
nums1nums2Почему с конца? Если записывать с начала, придётся сдвигать элементы nums1, что даст O(n²).
Сложность:
Условие:
Дан массив целых чисел nums. Переместите все нули в конец массива, сохраняя относительный порядок ненулевых элементов.
Выполните операцию in-place без создания копии массива.
Примеры:
Ввод: nums = [0, 1, 0, 3, 12] Вывод: [1, 3, 12, 0, 0]
Ввод: nums = [0] Вывод: [0]
Ограничения:
💡 Подсказка: Используйте два указателя:
slowуказывает на позицию для следующего ненулевого элемента,fastсканирует массив. Все ненулевые элементы «сжимаются» в начало.
Решение:
def moveZeroes(nums):
slow = 0
for fast in range(len(nums)):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1Или оптимизированная версия (меньше обменов):
def moveZeroes(nums):
slow = 0
# Сначала перемещаем все ненулевые элементы вперёд
for fast in range(len(nums)):
if nums[fast] != 0:
nums[slow] = nums[fast]
slow += 1
# Заполняем остаток нулями
for i in range(slow, len(nums)):
nums[i] = 0Объяснение:
slow указывает на позицию для следующего ненулевого элементаfast сканирует массивСложность:
Условие:
Дан массив nums из n объектов, окрашенных в красный, белый или синий цвет. Отсортируйте их in-place так, чтобы объекты одного цвета были рядом, а цвета шли в порядке: красный (0), белый (1), синий (2).
Используйте только константную дополнительную память.
Примеры:
Ввод: nums = [2, 0, 2, 1, 1, 0] Вывод: [0, 0, 1, 1, 2, 2]
Ввод: nums = [2, 0, 1] Вывод: [0, 1, 2]
Ограничения:
💡 Подсказка: Используйте три указателя (Dutch National Flag):
leftдля 0,rightдля 2,currentдля сканирования. 0 меняем сleft, 2 сright, 1 оставляем.
Решение (Dutch National Flag):
def sortColors(nums):
left, right = 0, len(nums) - 1
current = 0
while current <= right:
if nums[current] == 0:
nums[left], nums[current] = nums[current], nums[left]
left += 1
current += 1
elif nums[current] == 2:
nums[right], nums[current] = nums[current], nums[right]
right -= 1
# current не увеличиваем — нужно проверить swapped элемент
else: # nums[current] == 1
current += 1Объяснение:
left для 0, right для 2, current для сканированияleft, 2 с right, 1 оставляемleft — все 0, справа от right — все 2Сложность:
Условие:
Дана строка s. Можно удалить не более одного символа. Определите, может ли строка стать палиндромом после удаления.
Примеры:
Ввод: s = "aba" Вывод: true
Ввод: s = "abca" Вывод: true Объяснение: Можно удалить символ 'c'
Ввод: s = "abc" Вывод: false
Ограничения:
💡 Подсказка: Используйте стандартную проверку палиндрома. При несовпадении попробуйте два варианта: пропустить
leftилиright. Один из вариантов должен быть палиндромом.
Решение:
def validPalindrome(s):
def is_palindrome_range(left, right):
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return True
left, right = 0, len(s) - 1
while left < right:
if s[left] != s[right]:
# Пробуем удалить либо left, либо right
return is_palindrome_range(left + 1, right) or \
is_palindrome_range(left, right - 1)
left += 1
right -= 1
return TrueОбъяснение:
left или rightСложность:
Условие:
Дан массив целых чисел nums длины n и целевое значение target. Найдите все уникальные четвёрки [nums[a], nums[b], nums[c], nums[d]], такие что:
Верните массив всех уникальных четвёрок. Решение не должно содержать дублирующихся четвёрок.
Примеры:
Ввод: nums = [1, 0, -1, 0, -2, 2], target = 0 Вывод: [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]
Ввод: nums = [2, 2, 2, 2, 2], target = 8 Вывод: [[2, 2, 2, 2]]
Ограничения:
💡 Подсказка: Аналогично 3Sum: два внешних цикла для первых двух чисел, Two Pointers для оставшихся двух. Тщательная проверка дубликатов на всех уровнях.
Решение:
def fourSum(nums, target):
nums.sort()
result = []
n = len(nums)
for i in range(n - 3):
# Пропускаем дубликаты
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, n - 2):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
left, right = j + 1, n - 1
while left < right:
current_sum = nums[i] + nums[j] + nums[left] + nums[right]
if current_sum == target:
result.append([nums[i], nums[j], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
elif current_sum < target:
left += 1
else:
right -= 1
return resultОбъяснение:
Сложность:
Условие:
Дан массив неотрицательных целых чисел height, представляющий карту высот, где ширина каждого столбца равна 1. После дождя вода может удерживаться между столбцами.
Вычислите, сколько воды может удержаться после дождя.
Примеры:
Ввод: height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] Вывод: 6 Объяснение: Вода удерживается в 6 ячейках между столбцами
Ввод: height = [4, 2, 0, 3, 2, 5] Вывод: 9
Ограничения:
💡 Подсказка: Вода над столбцом = min(max_left, max_right) - height[i]. Используйте два указателя, двигая указатель с меньшей высотой. Поддерживайте максимумы с обеих сторон.
Решение (Two Pointers):
def trap(height):
if not height:
return 0
left, right = 0, len(height) - 1
left_max, right_max = 0, 0
water = 0
while left < right:
if height[left] < height[right]:
if height[left] >= left_max:
left_max = height[left]
else:
water += left_max - height[left]
left += 1
else:
if height[right] >= right_max:
right_max = height[right]
else:
water += right_max - height[right]
right -= 1
return waterОбъяснение:
min(max_left, max_right) - height[i]height[left] < height[right], то left_max точно ≤ right_max, поэтому вода определяется left_maxСложность:
Условие:
Даны две строки s и t. Верните true, если они равны при вводе в пустой текстовый редактор. Символ '#' обозначает backspace (удаление предыдущего символа).
Обратите внимание: если backspace применяется к пустой строке, она остаётся пустой.
Примеры:
Ввод: s = "ab#c", t = "ad#c" Вывод: true Объяснение: Обе строки превращаются в "ac"
Ввод: s = "ab##", t = "c#d#" Вывод: true Объяснение: Обе строки превращаются в ""
Ввод: s = "a#c", t = "b" Вывод: false Объяснение: s превращается в "c", t превращается в "b"
Ограничения:
💡 Подсказка: Идите с конца строки. Функция для нахождения следующего валидного символа учитывает backspace. Сравнивайте посимвольно.
Решение:
def backspaceCompare(s, t):
def get_next_valid_char_index(string, index):
backspace_count = 0
while index >= 0:
if string[index] == '#':
backspace_count += 1
elif backspace_count > 0:
backspace_count -= 1
else:
break
index -= 1
return index
i, j = len(s) - 1, len(t) - 1
while i >= 0 or j >= 0:
i = get_next_valid_char_index(s, i)
j = get_next_valid_char_index(t, j)
if i >= 0 and j >= 0 and s[i] != t[j]:
return False
# Если один индекс >= 0, а другой < 0 — строки разной длины
if (i >= 0) != (j >= 0):
return False
i -= 1
j -= 1
return TrueОбъяснение:
get_next_valid_char_index находит следующий символ, учитывая backspaceСложность:
Проблема: Использование left <= right вместо left < right для поиска пары.
# ❌ Неправильно
while left <= right: # Может привести к дублированию элементов
current = arr[left] + arr[right]
...
# ✅ Правильно для поиска пары
while left < right: # left и right никогда не совпадут
current = arr[left] + arr[right]
...Почему это неправильно: При left == right мы используем один и тот же элемент дважды, что нарушает условие задачи.
Как обнаружить: Проверьте, может ли решение вернуть один и тот же индекс дважды.
Проблема: Доступ к элементу массива без проверки границ.
# ❌ Неправильно — может выйти за границы
while left < right:
left += 1
if arr[left] == target: # left может быть == len(arr)!
...
# ✅ Правильно — сначала проверка, потом доступ
while left < right:
if arr[left] == target: # Проверка перед увеличением
...
left += 1Почему это неправильно: В Python это вызовет IndexError, в других языках — неопределённое поведение.
Как обнаружить: Всегда проверяйте, что индекс в диапазоне [0, len(arr)) перед доступом.
Проблема: Указатели не двигаются при определённых условиях.
# ❌ Неправильно — забыли двигать указатель
while left < right:
if condition:
pass # Забыли left += 1 или right -= 1!
else:
right -= 1
# ✅ Правильно — всегда двигаем хотя бы один указатель
while left < right:
if condition:
left += 1
else:
right -= 1Почему это неправильно: Если условие всегда истинно, указатель не двигается → бесконечный цикл.
Как обнаружить: Убедитесь, что на каждой итерации хотя бы один указатель изменяется.
Проблема: Применение Two Pointers к неотсортированному массиву.
# ❌ Неправильно — массив не отсортирован
nums = [3, 2, 4]
target = 6
left, right = 0, len(nums) - 1
# Two Pointers не работает!
# ✅ Правильно — сначала сортировка
nums.sort() # [2, 3, 4]
left, right = 0, len(nums) - 1
# Теперь Two Pointers работаетПочему это неправильно: Two Pointers полагается на монотонность: движение left увеличивает сумму, right — уменьшает.
Как обнаружить: Если массив не отсортирован, либо сортируйте, либо используйте Hash Map.
Проблема: Возврат дублирующихся троек в 3Sum.
# ❌ Неправильно — дубликаты в результате
for i in range(n):
left, right = i + 1, n - 1
while left < right:
if nums[i] + nums[left] + nums[right] == 0:
result.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
# ✅ Правильно — пропускаем дубликаты
nums.sort()
for i in range(n - 2):
if i > 0 and nums[i] == nums[i - 1]: # Пропуск дубликатов
continue
left, right = i + 1, n - 1
while left < right:
if nums[i] + nums[left] + nums[right] == 0:
result.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1 # Пропуск дубликатов
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1Почему это неправильно: Одинаковые элементы могут привести к дублирующимся результатам.
Как обнаружить: Проверьте результат на дубликаты. Сортируйте массив и пропускайте одинаковые значения.
Условие: Дан отсортированный массив целых чисел nums и целевое значение target. Найдите два числа, сумма которых равна target, и верните их значения (не индексы).
Если решения нет, верните [-1, -1].
Пример:
Ввод: nums = [1, 3, 5, 7, 9], target = 12
Вывод: [5, 7]
Используйте два указателя: один в начале (left = 0), другой в конце (right = len(nums) - 1).
Если сумма меньше target — увеличьте left (нужно больше). Если больше — уменьшите right (нужно меньше).
Цикл продолжается, пока left < right. Если нашли — верните [nums[left], nums[right]].
def two_sum_values(nums, target):
left, right = 0, len(nums) - 1
while left < right:
current_sum = nums[left] + nums[right]
if current_sum == target:
return [nums[left], nums[right]]
elif current_sum < target:
left += 1
else:
right -= 1
return [-1, -1]Объяснение:
Сложность:
| Задача | Паттерн | Ключевая идея |
|---|---|---|
| Two Sum II | Навстречу | Сумма краёв, двигаем к цели |
| Valid Palindrome | Навстречу | Сравнение с пропусками |
| Container With Most Water | Навстречу | Двигаем меньшую высоту |
| 3Sum/4Sum | Навстречу | Фиксация + Two Sum |
| Remove Duplicates | Fast & Slow | Копирование уникальных |
| Move Zeroes | Fast & Slow | Сжатие ненулевых |
| Sort Colors | Три указателя | Dutch National Flag |
| Trapping Rain Water | Навстречу | Вода = min(max_left, max_right) |
Two Pointers — фундаментальный паттерн, который:
Ключ к успеху:
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.