РУҚА
Тапсырмалар № 25, 26 · ЕГЭ

Обработка массива двумя указателями

Как двумя индексами находить отрезки, пары и группы элементов за линейное время
6 мин чтенияҚиындық: Обновлено 29 қыркүйек 2026

Метод двух указателей позволяет одновременно поддерживать левую и правую границы части массива и не перебирать один и тот же элемент много раз. Он особенно полезен для поиска непрерывных отрезков, пар и групп элементов, если при движении указателей условие задачи меняется предсказуемо.

Идея метода двух указателей

Пусть массив имеет индексы от \(0\) до \(n-1\). Два указателя обычно обозначают \(l\) и \(r\): \(l\) — левая граница текущего отрезка, \(r\) — правая. В каждый момент рассматривается отрезок \([l,r]\). В зависимости от задачи указатели могут двигаться навстречу друг другу, один за другим или независимо расширять и сужать окно.

D
Два указателя

Метод двух указателей — алгоритмический приём, в котором два индекса массива изменяются по правилу, сохраняющему полезную информацию о текущей паре, отрезке или группе элементов.

Главное свойство метода: указатель обычно движется только в одну сторону. Поэтому каждый индекс посещается не более нескольких раз, а время работы часто составляет \(O(n)\) вместо \(O(n^2)\). Перед применением нужно понять, почему после сдвига одного указателя можно безопасно исключить часть вариантов.

  • Для непрерывного отрезка указатели задают окно \([l,r]\).
  • Для пары в отсортированном массиве указатели ставят в начало и конец.
  • Для группировки один указатель обрабатывает позицию, другой хранит границу уже построенной группы.
  • Для каждого сдвига нужно доказать, какие варианты больше не могут быть ответом.
T
Когда метод работает за $O(n)

Если каждый из двух указателей перемещается только вперёд или только влево, а число его перемещений ограничено длиной массива, то общее число шагов не превосходит \(2n\) с точностью до константы. Поэтому сложность алгоритма равна \(O(n)\).

Скользящее окно: поиск подходящего отрезка

Скользящее окно применяют, когда условие зависит от всех элементов текущего непрерывного отрезка и его можно поддерживать при добавлении элемента справа и удалении элемента слева. Например, ищут самый длинный отрезок с суммой не больше \(S\), если все элементы массива неотрицательны.

Алгоритм расширяет окно указателем \(r\). Если условие нарушилось, указатель \(l\) двигается вправо, а из суммы удаляются элементы, которые покинули окно. После восстановления условия обновляется ответ.

\[\text{sum}=\sum_{i=l}^{r} a_i,\qquad \text{длина окна}=r-l+1\]

Для неотрицательных чисел сумма при увеличении \(r\) не уменьшается, а при увеличении \(l\) не возрастает. Именно эта монотонность позволяет не возвращать указатель назад.

Python
1def longest_segment(a, S):
2    left = 0
3    total = 0
4    best = 0
5
6    for right in range(len(a)):
7        total += a[right]
8        while total > S and left <= right:
9            total -= a[left]
10            left += 1
11        best = max(best, right - left + 1)
12
13    return best
!
Отрицательные элементы

Обычное скользящее окно для условия о сумме может быть неверным, если в массиве есть отрицательные числа. При удалении элемента слева сумма может увеличиться, а при добавлении справа — уменьшиться. В таком случае монотонность исчезает, и требуется другой алгоритм, например с префиксными суммами или структурой данных.

Если требуется найти количество отрезков, а не только максимальную длину, после восстановления условия можно добавить к ответу число вариантов правой границы. Такое преобразование зависит от точного условия, поэтому нельзя механически копировать формулу из другой задачи. Для задач о количестве элементов на отрезке полезно также знать подсчёт количества элементов на отрезке.

Проверь себя

Почему в алгоритме со скользящим окном указатель \(l\) не возвращается влево?

Пара элементов: указатели навстречу

Если массив отсортирован по возрастанию и нужно найти пару с суммой \(X\), поставим \(l=0\), \(r=n-1\). Рассмотрим сумму \(a_l+a_r\). Если она меньше \(X\), увеличить сумму можно только сдвигом \(l\) вправо: при фиксированном \(r\) все варианты с меньшим левым индексом дают ещё меньшую сумму. Если сумма больше \(X\), сдвигаем \(r\) влево.

\[a_l+a_r\begin{cases}<X,&l\leftarrow l+1\\>X,&r\leftarrow r-1\\=X,&\text{пара найдена}\end{cases}\]
T
Правило навстречу

В отсортированном массиве при поиске пары с заданной суммой, если текущая сумма меньше цели, левый указатель можно сдвинуть вправо; если больше цели — правый указатель можно сдвинуть влево. Все исключённые пары гарантированно не подходят.

Сортировка обычно занимает \(O(n\log n)\), а сам проход двумя указателями — \(O(n)\). Если исходный порядок элементов важен, вместе со значением хранят исходный индекс. Связь сортировки и указателей разобрана на странице «Сортировка и два указателя», а базовый нұсқа поиска пары — на странице поиска пары элементов.

Разобранный пример: максимальный отрезок с ограниченной суммой

Дан массив положительных чисел \([2,1,3,2,1,1,4]\) и число \(S=7\). Требуется найти длину самого длинного непрерывного отрезка, сумма которого не превосходит \(7\).

№
План шешімдер

Расширяем правую границу. Если сумма стала больше \(7\), удаляем элементы слева, пока условие снова не выполнится. После каждого шага сравниваем длину текущего окна с лучшим ответом.

1
Начинаем с пустого окна: \(l=0\), сумма равна нулю.
best=0
2
Добавляем \(2\): сумма \(2\), окно \([0,0]\).
\(\displaystyle 2\le 7,\quad best=1\)
3
Добавляем \(1\) и \(3\): сумма становится \(6\), окно \([0,2]\).
\(\displaystyle 2+1+3=6\le 7,\quad best=3\)
4
Добавляем следующий \(2\): сумма \(8\), условие нарушено. Удаляем \(a_0=2\).
\(\displaystyle 8-2=6,\quad [l,r]=[1,3],\quad best=3\)
5
Добавляем \(1\): сумма \(7\), окно \([1,4]\) допустимо.
\(\displaystyle 1+3+2+1=7,\quad best=4\)
6
Добавляем ещё \(1\): сумма \(8\). Удаляем слева \(1\), затем сумма \(7\).
\(\displaystyle 8-1=7,\quad [l,r]=[2,5],\quad best=4\)
7
Добавляем \(4\): сумма \(11\). Удаляем \(3\), затем \(2\), затем \(1\); остаётся \(4\).
\(\displaystyle 11-3-2-1=5,\quad [l,r]=[6,6]\)
8
Максимальная найденная длина равна четырём.
\(\displaystyle \boxed{\text{ответ}=4}\)

На шаге с последним элементом важно не остановиться после первого удаления: цикл должен удалять элементы, пока условие не станет истинным. Всего \(r\) прошёл массив один раз, а \(l\) также прошёл его не более бір раза, поэтому сложность равна \(O(n)\).

Группы, границы и два проходящих указателя

Другой нұсқа — разделение массива на группы по условию. Один указатель \(i\) просматривает элементы, а второй \(p\) указывает позицию, куда следует поставить следующий элемент нужной группы. Например, можно переместить все нулевые элементы в начало или все элементы, меньшие порога \(T\), влево.

Python
1def move_less_than_t(a, T):
2    p = 0
3    for i in range(len(a)):
4        if a[i] < T:
5            a[p], a[i] = a[i], a[p]
6            p += 1
7    return a

После обработки первых \(i\) элементов выполняется инвариант: в позициях от \(0\) до \(p-1\) находятся элементы, удовлетворяющие условию, а в позициях от \(p\) до \(i-1\) — уже просмотренные элементы, которые в первую группу не попали. Такой алгоритм работает за \(O(n)\) и использует \(O(1)\) дополнительной памяти.

Если требуется сохранить взаимный порядок элементов внутри групп, простой обмен может его нарушить. Тогда нужен стабильный алгоритм или дополнительный массив; подробнее о выборе подхода см. стабильную сортировку. Для группировки по конкретному значению полезна бет «Группировка по значению», а для условия — «Группировка по признаку».

!
Частые ошибки

1. Применять скользящее окно к отрицательным числам без доказательства монотонности. 2. Сдвигать указатель не в ту сторону в отсортированном массиве. 3. Забывать, что длина отрезка равна \(r-l+1\), а не \(r-l\). 4. Использовать только один шаг удаления вместо цикла while. 5. Терять исходные индексы после сортировки. 6. Считать, что любой алгоритм с двумя переменными \(l\) и \(r\) автоматически является методом двух указателей: нужно доказать, какие варианты исключаются.

Как оформлять Шешім на экзамене

Сначала укажите смысл каждого указателя. Затем запишите инвариант: что гарантированно верно внутри текущего окна или слева от границы. После этого объясните правило сдвига и оцените сложность. Такая структура помогает проверить не только код, но и доказательство корректности.

Связь с другими приёмами

Метод двух указателей не заменяет все алгоритмы для массивов. Для быстрых границ по уже обработанным элементам применяют префиксный минимум немесе префиксный максимум. Для трёх элементов часто фиксируют один индекс, а оставшиеся два ищут методом навстречу; это описано в поиске тройки элементов. Общая схема метода дана на странице «Метод двух указателей».

Q
Жылдам тест по теме

Быстрая проверка

~ 2 мин4 вопроса
Вопрос 1 / 4
Вопрос 1 из 4 · границы отрезка
Какова длина отрезка с границами \(l=3\) и \(r=8\)?
Главное за минуту

Главное

  • Два указателя задают границы окна, пару или границу группы; каждый указатель должен двигаться по доказанному правилу.
  • Для неотрицательных чисел скользящее окно находит подходящие отрезки за \(O(n)\), потому что левая и правая границы не возвращаются назад.
  • В отсортированном массиве указатели навстречу находят пару с заданной суммой за линейный проход после сортировки.
  • Перед применением метода нужно проверить монотонность и сформулировать инвариант.
  • Проверяйте включительность границ, сохранение индексов и стабильность порядка элементов.