Как ответить
Этот вопрос про типичную логику алгоритма с двумя указателями на отсортированном массиве, например, для поиска пары с заданной суммой. Суть в том, что мы держим инвариант: левый указатель (L) всегда строго меньше правого (R), а оба сдвигаются в зависимости от того, больше или меньше текущая сумма цели.
Кейс, когда сдвигается левый указатель — это ситуация, когда nums[L] + nums[R] < target. Поскольку массив отсортирован по возрастанию, единственный способ увеличить сумму — сдвинуть L вправо (увеличить левое слагаемое). Так мы не пропустим потенциально подходящую пару, потому что любые пары с текущим L и любым меньшим R уже дали бы ещё меньшую сумму (ведь все элементы слева от R не больше текущего).
while L < R:
if nums[L] + nums[R] == target:
# нашли пару
elif nums[L] + nums[R] < target:
L += 1 # сдвиг левого
else:
R -= 1 # сдвиг правогоКейс, когда сдвигается правый указатель — противоположный: nums[L] + nums[R] > target. Здесь сумма слишком велика, и чтобы её уменьшить, нужно сдвинуть R влево (уменьшить правое слагаемое). Это корректно, потому что правая сторона массива содержит бóльшие числа, и любая пара с текущим R и любым L правее текущего даст ещё бóльшую сумму.
Важные нюансы для middle-разработчика:
- Инвариант: L всегда увеличивается, R всегда уменьшается, что гарантирует завершение цикла за O(n) без пропуска пар — при условии, что массив отсортирован.
- Граничный случай «равно»: после нахождения цели можно сдвинуть оба указателя (L++, R--), если нужно найти все уникальные пары; иначе достаточно остановиться. Если требуется учитывать дубликаты, нужно дополнительно пропускать одинаковые значения.
- Бесконечный цикл: если не менять условие
L < Rи не сдвигать указатели при равенстве, цикл зациклится. При сдвиге всегда меняется только один указатель за итерацию, поэтому цикл корректен. - Пример ошибки: новички иногда пытаются сдвигать оба указателя, когда сумма меньше цели, думая «увеличим левый и уменьшим правый». Но это ломает логику — мы можем пропустить пару (L, R-1) или (L+1, R). Правильный сдвиг только одного указателя сохраняет все возможности.
Таким образом, ответ сводится к простому правилу: при < меньше цели — двигаем левый, при > цели — двигаем правый. Конкретный выбор диктуется направлением изменения суммы и монотонностью массива.