$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогАлгоритм «Два указателя»: решаем задачи на массивы быстро и элегантно

Алгоритм «Два указателя»: решаем задачи на массивы быстро и элегантно

$ sudo teach IT
·17 июля 2026 г.·8 мин·116
Алгоритм «Два указателя»: решаем задачи на массивы быстро и элегантно

Объясняем технику двух указателей на пяти классических задачах с разбором кода на Python. Идеальная подготовка к техническому собеседованию в IT-компанию.

Содержание
Что такое техника двух указателейЗадача 1. Два числа с заданной суммой (Two Sum II)УсловиеНаивное решениеРешение с двумя указателямиЗадача 2. Разворот строки на месте (Reverse String)УсловиеРешениеЗадача 3. Контейнер с наибольшим количеством воды (Container With Most Water)УсловиеРешениеЗадача 4. Три числа с нулевой суммой (3Sum)УсловиеРешениеЗадача 5. Удаление дубликатов из отсортированного массива (Remove Duplicates)УсловиеРешениеКогда техника двух указателей не работаетКак готовиться к собеседованию с этой техникойСводная таблица разобранных задачВывод

Техническое собеседование в IT-компанию — это не экзамен на знание синтаксиса языка. Интервьюер хочет понять, умеете ли вы думать об эффективности. Именно поэтому задачи на массивы появляются в каждом втором собеседовании: они компактны, хорошо масштабируются по сложности и мгновенно показывают, знаком ли кандидат с базовыми алгоритмическими техниками.

Одна из таких техник — два указателя (two pointers). Она не требует сложной математики и не занимает больше десяти строк кода. Но именно она отделяет решение за O(n²) от решения за O(n) — и именно это замечает интервьюер.

В этой статье разберём технику на пяти классических задачах с полным кодом на Python, объясним, почему она работает, и покажем типичные ошибки, которые допускают даже опытные разработчики.


Что такое техника двух указателей

Идея проста: вместо того чтобы перебирать все пары элементов вложенным циклом (O(n²)), мы заводим два индекса — левый и правый — и двигаем их навстречу друг другу или в одном направлении в зависимости от условия задачи. Это позволяет обойти массив за один проход — O(n).

Техника применима, когда:

  • массив отсортирован (или его можно отсортировать без потери смысла задачи);
  • нужно найти пару, тройку или подмассив с определённым свойством;
  • задача сводится к сужению диапазона поиска на основе сравнения элементов.

Существует два основных варианта:

  1. Встречные указатели — левый стартует с начала, правый с конца, движутся навстречу.
  2. Скользящее окно / быстрый и медленный указатель — оба движутся в одном направлении, но с разной скоростью или условием сдвига.

Разберём оба варианта на конкретных задачах.


Задача 1. Два числа с заданной суммой (Two Sum II)

Условие

Дан отсортированный массив целых чисел и целевое число target. Найти индексы двух элементов, сумма которых равна target. Индексы возвращаются с единицы (1-based).

Наивное решение

code
def two_sum_naive(numbers, target):
    n = len(numbers)
    for i in range(n):
        for j in range(i + 1, n):
            if numbers[i] + numbers[j] == target:
                return [i + 1, j + 1]

Сложность: O(n²) по времени. На массиве из 10 000 элементов — уже 50 миллионов итераций.

Решение с двумя указателями

code
def two_sum(numbers, target):
    left, right = 0, len(numbers) - 1

    while left < right:
        current_sum = numbers[left] + numbers[right]

        if current_sum == target:
            return [left + 1, right + 1]
        elif current_sum < target:
            left += 1   # сумма слишком мала — берём большее число слева
        else:
            right -= 1  # сумма слишком велика — берём меньшее число справа

    return []  # решения нет

Почему это работает: массив отсортирован, поэтому если текущая сумма меньше цели — нужно увеличить левый элемент (сдвинуть left вправо). Если больше — уменьшить правый (сдвинуть right влево). Мы никогда не пропустим правильную пару, потому что на каждом шаге исключаем заведомо неподходящие варианты.

Сложность: O(n) по времени, O(1) по памяти.


Задача 2. Разворот строки на месте (Reverse String)

Условие

Развернуть массив символов in-place, не используя дополнительную память.

Решение

code
def reverse_string(s):
    left, right = 0, len(s) - 1

    while left < right:
        s[left], s[right] = s[right], s[left]
        left += 1
        right -= 1

Классический пример встречных указателей. Мы меняем крайние элементы местами и двигаемся к центру. Когда left >= right — массив развёрнут.

code
s = ['h', 'e', 'l', 'l', 'o']
reverse_string(s)
print(s)  # ['o', 'l', 'l', 'e', 'h']

Сложность: O(n) по времени, O(1) по памяти. Никаких срезов s[::-1] — здесь мы работаем in-place, что принципиально важно в задачах с ограничением по памяти.


Задача 3. Контейнер с наибольшим количеством воды (Container With Most Water)

Условие

Дан массив высот height, где height[i] — высота вертикальной линии в точке i. Найти два индекса i и j (i < j), при которых площадь «контейнера» (минимальная из двух высот, умноженная на расстояние между ними) максимальна.

Решение

code
def max_area(height):
    left, right = 0, len(height) - 1
    max_water = 0

    while left < right:
        # Ширина контейнера
        width = right - left
        # Высота ограничена меньшей из двух стенок
        current_area = min(height[left], height[right]) * width
        max_water = max(max_water, current_area)

        # Сдвигаем указатель со стороны меньшей высоты
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1

    return max_water

Ключевой инсайт: всегда сдвигаем указатель с меньшей высотой. Если сдвинуть указатель с большей высотой, ширина уменьшится, а высота не вырастет (она всё равно ограничена меньшей стенкой). Значит, площадь только уменьшится. Сдвигая меньшую сторону, мы даём шанс найти более высокую стенку и потенциально увеличить площадь.

code
print(max_area([1, 8, 6, 2, 5, 4, 8, 3, 7]))  # 49

Задача 4. Три числа с нулевой суммой (3Sum)

Условие

Найти все уникальные тройки в массиве, сумма элементов которых равна нулю.

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

Решение

code
def three_sum(nums):
    nums.sort()
    result = []
    n = len(nums)

    for i in range(n - 2):
        # Пропускаем дубликаты для первого элемента
        if i > 0 and nums[i] == nums[i - 1]:
            continue

        left, right = i + 1, n - 1

        while left < right:
            total = nums[i] + nums[left] + nums[right]

            if total == 0:
                result.append([nums[i], nums[left], nums[right]])

                # Пропускаем дубликаты для второго и третьего элементов
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1

                left += 1
                right -= 1

            elif total < 0:
                left += 1
            else:
                right -= 1

    return result
code
print(three_sum([-1, 0, 1, 2, -1, -4]))
# [[-1, -1, 2], [-1, 0, 1]]

Типичная ошибка: забыть про обработку дубликатов. Без пропуска повторяющихся элементов алгоритм вернёт одинаковые тройки несколько раз. Обратите внимание на три места, где мы проверяем дубликаты: для i, для left и для right.

Сложность: O(n²) — сортировка O(n log n) плюс вложенный цикл O(n²). Это оптимально для данной задачи: доказано, что лучше O(n²) для 3Sum не достичь без дополнительных ограничений.


Задача 5. Удаление дубликатов из отсортированного массива (Remove Duplicates)

Условие

Дан отсортированный массив. Удалить дубликаты in-place так, чтобы каждый элемент встречался только один раз. Вернуть количество уникальных элементов.

Здесь оба указателя движутся в одном направлении — это второй классический паттерн техники.

Решение

code
def remove_duplicates(nums):
    if not nums:
        return 0

    # slow — граница уникальных элементов
    # fast — сканирует весь массив
    slow = 0

    for fast in range(1, len(nums)):
        if nums[fast] != nums[slow]:
            slow += 1
            nums[slow] = nums[fast]

    return slow + 1
code
nums = [1, 1, 2, 3, 3, 4]
k = remove_duplicates(nums)
print(k)         # 4
print(nums[:k])  # [1, 2, 3, 4]

Как это работает: slow отслеживает позицию последнего записанного уникального элемента. fast бежит вперёд. Как только fast находит элемент, отличный от nums[slow], мы продвигаем slow и записываем туда новый уникальный элемент. Дубликаты просто перезаписываются или остаются за границей slow.

Это паттерн «медленный и быстрый указатель» — он же используется для поиска цикла в связном списке (алгоритм Флойда), но это уже тема отдельной статьи.


Когда техника двух указателей не работает

Важно понимать ограничения метода, чтобы не применять его там, где он не подходит.

Неотсортированные данные без возможности сортировки. Если порядок элементов важен для задачи (например, нужно найти подмассив с максимальной суммой с сохранением порядка), сортировать нельзя. Здесь нужны другие техники: динамическое программирование или алгоритм Кадане.

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

Связные списки с произвольным доступом. Для связных списков два указателя работают только в специфических сценариях (поиск середины, цикл), потому что нет индексного доступа к элементам.


Как готовиться к собеседованию с этой техникой

Знание паттерна — это половина дела. На собеседовании важно уметь объяснить, почему вы выбрали именно этот подход. Типичная структура ответа:

  1. Назвать наивное решение и его сложность.
  2. Объяснить, какое свойство задачи позволяет применить два указателя.
  3. Показать инвариант: что остаётся истинным на каждом шаге цикла.
  4. Разобрать граничные случаи: пустой массив, один элемент, все элементы одинаковые.

Для систематической подготовки к таким задачам стоит изучить не только паттерны, но и математическую основу их корректности. Если вы хотите разобраться в этом структурированно — курс «Алгоритмы и Структуры Данных на Python» охватывает именно этот пласт: от анализа сложности до реализации классических алгоритмов с разбором типичных задач с интервью.


Сводная таблица разобранных задач

ЗадачаПаттернСложность по времениСложность по памяти
Two Sum IIВстречные указателиO(n)O(1)
Reverse StringВстречные указателиO(n)O(1)
Container With Most WaterВстречные указателиO(n)O(1)
3SumСортировка + встречныеO(n²)O(1)
Remove DuplicatesМедленный + быстрыйO(n)O(1)

Вывод

Техника двух указателей — один из тех инструментов, которые после изучения начинаешь замечать повсюду. Она лежит в основе десятков задач на LeetCode, регулярно встречается на собеседованиях в компании уровня FAANG и российских IT-лидеров, и при этом остаётся концептуально простой.

Главное, что нужно усвоить: два указателя работают не магически, а за счёт чёткого инварианта — свойства, которое сохраняется на каждом шаге и гарантирует, что мы не пропустим правильный ответ. Как только вы научитесь формулировать этот инвариант вслух, объяснять решение на собеседовании станет значительно проще.

Следующий шаг — практика. Решите каждую из пяти задач самостоятельно, без подглядывания в код. Затем попробуйте задачи-вариации: Trapping Rain Water, Sort Colors (флаг Нидерландов), Minimum Size Subarray Sum. Если хочется двигаться по структурированной программе с разбором теории и задач — курс «Алгоритмы и Структуры Данных на Python» может стать хорошей точкой опоры для этой подготовки.

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Алгоритмы и Структуры Данных на Python

Курс по теме

Алгоритмы и Структуры Данных на Python

Алгоритмы и структуры данных — ключевой этап отбора в крупные IT-компании. На курсе изучите списки, словари, множества, линейный поиск, два указателя, скользящее окно. Этого хватит для успешного прохождения вашего первого собеседования.

Открыть курс

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Solidity DLC - поймай, если сможешь

Solidity DLC - поймай, если сможешь

Аудит смарт-контрактов на реальных взломах. The DAO, Parity, Wormhole, Ronin, Euler и ещё 7 разборов: что пошло не так, сколько потеряли и что бы спасло. Атаки и защиты на Foundry, план входа в профессию аудитора.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Solidity pro - всё или ничего

Solidity pro - всё или ничего

Как пишут контракты, которые работают в основной сети. Наследование и OpenZeppelin, паттерны продакшена, оптимизация газа, ABI, delegatecall и подписи. 7 модулей, 33 урока, а в финале собственное dApp: мини-DAO с токеном, голосованием и казной.

Перейти к курсу

Другие статьи

FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь
fastapi

FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь

Поймём различия между синхронной обработкой, BackgroundTasks и внешними очередями. Разберём idempotency, ретраи и мониторинг фоновых процессов.

22 июля 2026 г.
1160
FastAPI и фоновые задачи: очередь vs BackgroundTasks, idempotency и обработка ретраев
fastapi

FastAPI и фоновые задачи: очередь vs BackgroundTasks, idempotency и обработка ретраев

На примерах объясним, когда достаточно BackgroundTasks, а когда нужна отдельная очередь/воркер. Разберём, как обеспечить воспроизводимость выполнения, безопасные ретраи и корректные статусы задач в API.

2 августа 2026 г.
920
Как выучить программирование самому и бесплатно: с чего начать и в каком порядке
выучить

Как выучить программирование самому и бесплатно: с чего начать и в каком порядке

Разберём понятный маршрут для новичка: как выбрать первый язык, что учить в первую очередь, какие мини-проекты делать и как не забросить занятия. Дам бесплатные ориентиры и план, чтобы знания складывались в портфолио.

21 сентября 2026 г.
1970
Как начать с HTML и сделать первую страницу: пошагово и без сложных слов
html

Как начать с HTML и сделать первую страницу: пошагово и без сложных слов

Разберём, что нужно новичку, чтобы за 1–2 вечера собрать свою первую веб-страницу: структура HTML, базовые теги, ссылки и картинки, плюс простой чек-лист, как понять, что страница работает правильно.

2 ноября 2026 г.
20
Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей
cargo

Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей

Разберём, как организовать Rust workspace, управлять optional features, фиксировать версии библиотек и уменьшать “дрейф” зависимостей при командной разработке и CI.

18 сентября 2026 г.
1960
Что такое HTML и чем он отличается от CSS — объясню простыми словами и на примерах
такое

Что такое HTML и чем он отличается от CSS — объясню простыми словами и на примерах

Разберём, из чего состоит веб-страница: что делает HTML (смысл и структура) и что делает CSS (оформление). В конце соберёте мини-страницу и поймёте, что нужно учить в каком порядке.

30 октября 2026 г.
130

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

Что такое техника двух указателейЗадача 1. Два числа с заданной суммой (Two Sum II)УсловиеНаивное решениеРешение с двумя указателямиЗадача 2. Разворот строки на месте (Reverse String)УсловиеРешениеЗадача 3. Контейнер с наибольшим количеством воды (Container With Most Water)УсловиеРешениеЗадача 4. Три числа с нулевой суммой (3Sum)УсловиеРешениеЗадача 5. Удаление дубликатов из отсортированного массива (Remove Duplicates)УсловиеРешениеКогда техника двух указателей не работаетКак готовиться к собеседованию с этой техникойСводная таблица разобранных задачВывод