РУҚА
Задания № 17, 26 · ЕГЭ

Два указателя в отсортированном массиве

Как находить пары и отрезки в отсортированном массиве за линейное время
6 мин чтенияСложность: Обновлено 29 сентября 2026

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

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

Пусть дан массив \(a\), элементы которого расположены по неубыванию: \(a_0 \le a_1 \le \dots \le a_{n-1}\). Два указателя — это обычно два индекса: левый \(l\) и правый \(r\). Они обозначают границы рассматриваемого участка или двух элементов массива.

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

Два указателя — это два индекса, которые перемещаются по массиву по заданному правилу. Каждый указатель обычно движется только в одну сторону, поэтому общее число перемещений не превышает \(2n\).

В задачах на пару указатели стоят на концах массива: \(l=0\), \(r=n-1\). Рассматривается сумма \(a_l+a_r\). В задачах на отрезок они задают окно \([l,r]\), внутри которого поддерживается условие, например ограничение на сумму.

lra₀a₁...aₙ₋₂aₙ₋₁
Указатели l и r на концах отсортированного массива.

Поиск пары с заданной суммой

Требуется найти два различных элемента, сумма которых равна \(S\). Сначала берём крайние элементы. Если сумма слишком мала, нужно увеличить её: в отсортированном массиве это можно сделать только увеличением \(l\). Если сумма слишком велика, уменьшаем её уменьшением \(r\).

\[s=a_l+a_r\]
T
Правило движения указателей

Для отсортированного массива при поиске суммы \(S\): если \(a_l+a_r<S\), увеличить \(l\); если \(a_l+a_r>S\), уменьшить \(r\); если \(a_l+a_r=S\), пара найдена.

Почему нельзя потерять решение? Если \(a_l+a_r<S\), то для текущего \(l\) любой индекс справа от \(r\) даст сумму не меньше, а при движении вправо сумма не уменьшится. Значит, текущий \(l\) с любым индексом правее \(r\) не подходит, и его можно исключить. Аналогично при слишком большой сумме исключается текущий \(r\).

1
Начинаем с крайних элементов массива.
\(\displaystyle l=0,\quad r=n-1\)
2
Вычисляем текущую сумму.
\(\displaystyle s=a_l+a_r\)
3
Если сумма меньше цели, увеличиваем левую границу.
\(\displaystyle s<S\Rightarrow l\leftarrow l+1\)
4
Если сумма больше цели, уменьшаем правую границу.
\(\displaystyle s>S\Rightarrow r\leftarrow r-1\)
5
Останавливаемся при совпадении или пересечении указателей.
\(\displaystyle s=S\ \text{или}\ l\ge r\)
Python
1def find_pair(a, target):
2    left = 0
3    right = len(a) - 1
4    while left < right:
5        current = a[left] + a[right]
6        if current == target:
7            return left, right
8        if current < target:
9            left += 1
10        else:
11            right -= 1
12    return None
Микро-проверка

Массив \([2, 4, 7, 9, 12]\), цель \(14\), указатели стоят на \(2\) и \(12\). Что делать после первого сравнения?

№
Разобранный пример: пара с заданной суммой

Дан массив \([1,3,4,6,8,11]\) и число \(S=14\). Нужно определить, существуют ли два различных элемента с такой суммой.

1
Ставим указатели на первый и последний элементы.
\(\displaystyle l=0,\ r=5,\ a_l+a_r=1+11=12\)
2
Сумма меньше \(14\), поэтому увеличиваем левый указатель.
\(\displaystyle l\leftarrow1,\quad 3+11=14\)
3
Получили нужную сумму.
\(\displaystyle a_1+a_5=3+11=14\)

Ответ: пара найдена — элементы \(3\) и \(11\), их индексы \(1\) и \(5\) при нумерации с нуля. Алгоритм сделал только два сравнения, хотя полный перебор проверял бы все пары.

Поиск отрезка с ограничением

Другой вариант — поиск непрерывного отрезка, например самого длинного отрезка с суммой не больше \(K\). Здесь \([l,r]\) называют скользящим окном. Метод особенно удобен, если все элементы массива неотрицательны.

T
Скользящее окно для неотрицательных чисел

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

Алгоритм: правый указатель последовательно проходит массив. Каждый новый элемент прибавляется к текущей сумме. Пока сумма больше \(K\), из окна удаляется элемент \(a_l\), а \(l\) увеличивается. После этого обновляется ответ, например длина окна \(r-l+1\).

\[\text{длина окна}=r-l+1\]
Псевдокод
left := 0
sum := 0
answer := 0
for right := 0..n-1 do
    sum := sum + a[right]
    while sum > K do
        sum := sum - a[left]
        left := left + 1
    answer := max(answer, right - left + 1)

Каждый элемент сначала один раз добавляется в окно, а затем не более одного раза удаляется из него. Значит, несмотря на вложенный цикл while, время работы равно \(O(n)\), а не \(O(n^2)\).

Строки и два указателя

Та же идея применяется к строкам. Например, можно сравнивать строку слева направо, используя индексы \(i\) и \(j\), искать совпадающие символы или проверять, является ли последовательность подстрокой. Для работы с отдельными символами важно понимать, что такое индекс символа, а последовательный просмотр выполняется методом перебора строки.

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

№
Проверка вхождения последовательности

Пусть дана отсортированная строка символов и нужно найти, есть ли в ней символ \(x\). Указатель движется слева направо: если текущий символ меньше \(x\), переходим дальше; если равен — останавливаемся; если больше — поиск можно завершить, потому что все следующие символы ещё больше.

Сложность и условия применимости

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

\[T(n)=O(n\log n)+O(n)=O(n\log n)\]

Для скользящего окна условие монотонности особенно важно. Если в массиве есть отрицательные числа, добавление элемента справа может уменьшить сумму, а удаление слева — увеличить её. Простое правило «сдвигать левую границу, пока сумма велика» тогда может быть неверным. В таких задачах используют другие методы: префиксные суммы, словари или специальные структуры данных.

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

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

Как выбрать направление движения

Сначала запишите, как изменение каждого указателя влияет на величину, которую сравниваете с целью. Если увеличение \(l\) увеличивает сумму, а уменьшение \(r\) уменьшает её, правило становится почти очевидным.

Алгоритм решения задания

  1. Проверьте, отсортирован ли массив. Если нет — выясните, разрешена ли сортировка и не требуется ли сохранить исходные индексы.
  2. Определите, что обозначают указатели: два элемента или границы окна.
  3. Запишите величину, которую сравниваете с целью: сумму, разность, длину или другое условие.
  4. Докажите направление движения: какой указатель изменяет величину в нужную сторону.
  5. Укажите границу цикла: обычно \(l<r\) для пары или \(r<n\) для окна.
  6. Проверьте крайние случаи: пустой массив, один элемент, отсутствие ответа, повторяющиеся значения.
F
Флешкарта
Что делать, если \(a_l+a_r<S\)?
Увеличить \(l\), потому что массив отсортирован по возрастанию.
Q
Быстрый тест по теме

Проверь себя

~ 2 мин4 вопроса
Вопрос 1 / 4
Вопрос 1 из 4 · сложность
Какова сложность поиска пары двумя указателями в уже отсортированном массиве?
Главное за минуту

Главное

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