---
title: "Два указателя (с концов)"
url: https://algopath.pro/ru/patterns/two-pointers-opposite
language: ru
summary: "Два индекса стартуют по краям отсортированного массива и идут навстречу. Каждый шаг двигает ту сторону, что мешает ответу."
updated: 2026-08-24
---

# Два указателя (с концов)

Два индекса стартуют по краям отсортированного массива и идут навстречу. Каждый шаг двигает ту сторону, что мешает ответу.

## Два указателя (с концов): как это работает?

Один индекс стоит на первой ячейке, другой на последней. Вместе они задают кандидата на ответ.

Прочитайте значения на обоих концах и сложите их. Получится сумма, ширина или пара для сравнения.

Сравните результат с целью. Сравнение говорит, какой конец мешает.

Если комбинация мала, сдвиньте левый индекс вправо. Порядок гарантирует, что значение только вырастет.

Если она велика, сдвиньте правый индекс влево. Это единственный ход, который её уменьшит.

Цикл кончается, когда индексы встретились. Каждый элемент прочитан максимум один раз.

- `[2, 4, 5, 8, 11] lo=0 hi=4` Цель равна 12. Указатели стартуют по краям.
- `2 + 11 = 13` Сумма на единицу больше цели. Уменьшить её может только правый конец.
- `[2, 4, 5, 8, 11] lo=0 hi=3` Теперь 2 плюс 8 это 10, и этого мало. Увеличить может только левый конец.
- `[2, 4, 5, 8, 11] lo=1 hi=3` Теперь 4 плюс 8 это 12. Цель достигнута точно.
- `ответ = [1, 3]` Три сравнения на пять элементов. Ничего не прочитано дважды.

## Два указателя (с концов): когда применять?

- отсортированный массив (или можно отсортировать)
- найти пару/тройку с заданной суммой
- сжать с обоих концов (контейнер, площадь)
- проверка палиндрома

## Два указателя (с концов): с чем путают?

- **Скользящее окно (переменное)** - Окно хранит живой отрезок, и оба конца идут вперёд. Эти указатели стартуют врозь и сходятся.
- **Хеш-множество / словарь** - Хеш находит дополнение в неотсортированных данных ценой O(n) памяти. Указателям нужен порядок, но не память.
- **Два указателя (в одну сторону)** - Там оба индекса идут вперёд: один читает, другой пишет. Здесь они сближаются с разных концов.
- **Бинарный поиск (массив)** - Бинарный поиск ищет одну фиксированную цель и делит отрезок пополам. Здесь сравниваемая пара меняется каждый шаг.

## Два указателя (с концов): сложность по времени и памяти

n до 1e6 на отсортированных данных даёт O(n). Каждый указатель делает не больше n шагов, память O(1).

## Два указателя (с концов): разбор примера

### Контейнер с наибольшей водой

Каждое число это высота вертикальной линии на графике. Выберите две линии, между которыми поместится больше всего воды.

Площадь равна меньшей из линий, умноженной на расстояние между ними.

Начните с самой широкой пары, по линии на каждом краю.

Сдвигать более высокую линию бессмысленно: площадь ограничена низкой. Значит двигайте низкую и запоминайте лучшую площадь.

```javascript
function maxArea(height) {
    let lo = 0;
    let hi = height.length - 1;
    let best = 0;

    while (lo < hi) {
        const area = Math.min(height[lo], height[hi]) * (hi - lo);
        best = Math.max(best, area);

        // the shorter side caps the area, so it is the only one worth moving
        if (height[lo] < height[hi]) lo++;
        else hi--;
    }

    return best;
}
```

## Два указателя (с концов): частые ошибки

- **Забывают, что вход обязан быть отсортирован** Весь приём держится на порядке. На неотсортированных данных сравнение указывает не туда.
- **Пишут lo <= hi** Тогда индекс составит пару сам с собой. Для пары разных элементов нужно lo < hi.
- **Двигают оба конца за один шаг** Тогда верная пара может быть пропущена без проверки. Двигайте ровно один конец за шаг.
- **Выдают одну тройку дважды** После совпадения пропускайте равные значения с обеих сторон. Иначе дубликаты вернутся как разные ответы.

## Два указателя (с концов): задачи с собеседований

- **Сумма двух II (отсортированный вход)** Базовая форма: двигайте тот конец, что чинит сумму.
- **Сумма трёх** Зафиксируйте одно значение и пустите два указателя по остатку.
- **Контейнер с наибольшей водой** Двигайте низкую сторону, она ограничивает площадь.
- **Проверка палиндрома** Сравните концы и шагните внутрь.
- **Сбор дождевой воды** Двигайте сторону с меньшей стенкой и копите воду.
- **Сортировка цветов** Два конца собирают нули и двойки.
- **Квадраты отсортированного массива** Наибольший квадрат всегда лежит на одном из концов.

## JavaScript

```javascript
function twoPointers(arr, target) {
    let lo = 0;
    let hi = arr.length - 1;
    while (lo < hi) {
        const sum = arr[lo] + arr[hi];
        if (sum === target) return [lo, hi];
        if (sum < target) lo++;
        else hi--;
    }
    return null;
}
```

## Python

```python
def two_pointers(arr, target):
    lo, hi = 0, len(arr) - 1
    while lo < hi:
        s = arr[lo] + arr[hi]
        if s == target:
            return (lo, hi)
        if s < target:
            lo += 1
        else:
            hi -= 1
    return None
```

## PHP

```php
function twoPointers(array $arr, int $target): ?array {
    $lo = 0;
    $hi = count($arr) - 1;
    while ($lo < $hi) {
        $sum = $arr[$lo] + $arr[$hi];
        if ($sum === $target) return [$lo, $hi];
        if ($sum < $target) $lo++;
        else $hi--;
    }
    return null;
}
```
