Признак в условии
- вход отсортирован
- «пара с суммой», «палиндром», «удалить на месте»
- нужно O(1) дополнительной памяти
- надо сравнивать элементы с разных концов
Идея
Два индекса вместо вложенного цикла. Навстречу - когда вход отсортирован и есть монотонность. Быстрый и медленный - когда надо писать и читать в один массив или искать середину.
Шаблон
let l = 0, r = nums.length - 1;
while (l < r) {
const sum = nums[l] + nums[r];
if (sum === target) return [l, r];
if (sum < target) l++; // нужно больше
else r--; // нужно меньше
}
let write = 0; // чтение/запись
for (let read = 0; read < nums.length; read++) {
if (keep(nums[read])) nums[write++] = nums[read];
}
return write;
Сложность
Время O(n), память O(1). Если нужна сортировка - O(n log n).
Частые ошибки
- внутренние циклы пропуска не проверяют
l < rи уходят за границу - двигают не тот указатель (в задаче про воду - надо двигать меньшую сторону)
- забыт пропуск дублей, из-за чего в ответе повторяющиеся тройки
Мнемоника
«Отсортировано - значит с двух концов»
Не путать с
- Скользящее окно - окно копит состояние внутри (счётчики, сумма); два указателя ничего не копят, они сходятся или идут в одну сторону, вытесняя элементы
- Бинарный поиск - оба любят отсортированный вход, но бинарный поиск выбрасывает половину за шаг, а указатели проходят массив один раз
- Связный список - быстрый и медленный указатели на списке - это те же два указателя, только без индексов
Задачи на этот паттерн
- пара с суммой в отсортированном массиве
- палиндром двумя указателями
- сдвинуть нули в конец
- удалить дубли на месте
- контейнер с водой
- тройки с нулевой суммой
- сортировка трёх значений
- квадраты отсортированного массива
- строки с забоем
- слияние в первый массив