Тренировки по алгоритмам от Яндекса. Лекция 5: «Префиксные суммы и два указателя»

YOUTUBE · 16.11.2025 08:22

Ключевые темы и таймкоды

Введение в префиксные суммы

0:09
  • Лекция посвящена префиксным суммам и методу двух указателей.
  • Префиксные суммы полезны для аналитики, особенно для подсчёта событий за определённый интервал времени.

Определение префиксных сумм

0:20
  • Префиксные суммы позволяют быстро подсчитывать сумму элементов на полуинтервалах.
  • В Python часто используются полуинтервалы, поэтому рассматривается их применение.
  • Для быстрого ответа на запросы необходимо предварительно подсчитать массив префиксных сумм.

Построение массива префиксных сумм

1:39
  • Длина массива префиксных сумм на один больше длины исходного массива.
  • Элемент массива префиксных сумм содержит сумму всех чисел из исходного массива с индексами от 0 до текущего индекса минус один.
  • Пример: в префиксной сумме 1 лежит число 5, в префиксной сумме 0 — ноль, в префиксной сумме 2 — число 8, сумма чисел 3 и 5.

Метод вычисления префиксных сумм

2:43
  • Префиксную сумму можно вычислить, добавляя очередной элемент к предыдущей префиксной сумме.
  • Пример: складывание 5 и 0 даёт 5, складывание 5 и 3 даёт 8.

Ошибки при работе с префиксными суммами

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

Решение запросов с помощью префиксных сумм

8:02
  • Для ответа на запрос нужно вычесть префиксную сумму с правой границы запроса из префиксной суммы с левой границы.
  • Пример: из 21 вычесть 8 даёт 13.
  • Полуинтервалы позволяют избежать проблемы с запросами, прижатыми к левому краю.

Оптимизация кода

11:15
  • На языках без компиляторной оптимизации вызовы функций могут замедлять решение.
  • Вместо вызовов функций можно использовать выражения для ускорения работы.
  • Компиляторы с оптимизацией могут автоматически оптимизировать код.

Применение префиксных сумм в аналитике

12:27
  • Пример применения: подсчёт количества посетителей сайта за определённый интервал времени.
  • Каждый посетитель приносит единицу посещения, и можно подсчитать количество посетителей за каждый день.
  • Построение массива префиксных сумм позволяет быстро отвечать на запросы о посещаемости.

Задача о подсчёте нулей на полуинтервале

13:27
  • Дана последовательность чисел и запросы к ней.
  • Задача: подсчитать количество нулей на полуинтервале от i до r, где i не включительно.
  • Простое решение: перебрать все числа и подсчитать нули.
  • Сложность: O(n) для каждого запроса.

Применение префиксных сумм

14:48
  • Вместо префиксной суммы считаем количество нулей на префиксе.
  • Пример: массив префиксных нулей для последовательности.
  • Вычитание префиксных сумм для ответа на запрос.
  • Сложность: O(n) на построение массива.

Задача о отрезках с нулевой суммой

17:06
  • Дана последовательность чисел, нужно найти количество отрезков, сумма на которых равна нулю.
  • Пример применения: анализ доходности акций.
  • Наивное решение: перебрать левую и правую границы, суммировать элементы между ними.
  • Сложность: O(n³).

Оптимизация наивного решения

18:58
  • Фиксируем левую границу и двигаемся направо, прибавляя очередное число к текущей сумме.
  • Если сумма равна нулю, увеличиваем счётчик.
  • Сложность: O(n²).

Нормальное решение с префиксными суммами

21:00
  • Считаем массивы префиксных сумм.
  • Если суммы в двух местах массива совпадают, между ними сумма равна нулю.
  • Используем словарь для подсчёта количества появлений префиксных сумм.
  • Сложность: O(n).

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

25:36
  • Словарь эффективен для больших префиксных сумм.
  • Массив неэффективен из-за неизвестного размера.

Задача с двумя указателями

26:23
  • Дана отсортированная последовательность чисел и число k.
  • Нужно найти количество пар, разность в которых больше k.
  • Пример: последовательность 1, 3, 7, 8, k = 4.
  • Подходящие пары: 1-7, 1-8, 3-8.
  • Ответ: 3 пары.

Введение в задачу

27:41
  • Обсуждение ситуации, когда после курса по алгоритмам решение задачи кажется очевидным, но интуиция подсказывает, что оно не соответствует ожиданиям.
  • Подчёркивание важности наличия хоть какого-то решения, даже если оно не идеально.

Описание решения

28:20
  • Перебор всех пар чисел и проверка условия: разница между числами должна быть больше k.
  • Использование упорядоченности массива для эффективного перебора.

Особенности правильного решения

29:14
  • Правильное решение может быть длиннее неправильного, но оно более эффективно.
  • Пример с массивом {1, 3, 7, 8} и k = 4 иллюстрирует принцип работы.

Принцип работы с указателями

30:11
  • Фиксация левого числа и переборка правого до первого подходящего числа.
  • При обнаружении подходящего числа добавление его к ответу без перебора остальных.

Вторая хитрость

31:58
  • При переходе к следующему числу правый указатель двигается с текущего положения.
  • Если число не подходит для текущего левого числа, оно не подходит и для следующего.

Реализация алгоритма

33:09
  • Левый указатель двигается на единицу, правый — до первого подходящего числа.
  • Проверка условий выхода за пределы массива и добавление длины подходящего хвоста к ответу.

Важность тестирования

35:31
  • Необходимость тестирования программы на худший случай.
  • Оптимизации могут замедлить программу, поэтому важно учитывать все аспекты.

Заключение

36:46
  • Подчёркивание важности экспериментов на реальных данных для оценки эффективности оптимизаций.
  • Упоминание о различных способах движения двух указателей в зависимости от задачи.

Задача про игроков

36:59
  • Необходимо сформировать команду из игроков, у каждого из которых есть число, отражающее его профессионализм.
  • Команда должна быть сплочённой: два самых слабых игрока в сумме играют не хуже, чем самый сильный.
  • Цель — найти самую сильную и сплочённую команду.

Сортировка игроков

37:40
  • Игроки сортируются по возрастанию профессионализма.
  • В реальной задаче нужно также указать номера игроков.

Формирование команды

38:38
  • Выбираем самого сильного игрока и формируем команду, добавляя всех подходящих игроков.
  • Если профессионализм игрока отрицательный, его не берём в команду.

Принцип сплочённости

39:38
  • Сплочённая команда характеризуется умением играть самого слабого игрока.
  • После выбора самого слабого игрока добавляем всех промежуточных игроков.

Использование двух указателей

40:46
  • Перебираем самого сильного игрока, двигая левый указатель до тех пор, пока сумма умений двух самых слабых игроков больше, чем у самого сильного.
  • Если сумма меньше, не добавляем игрока.

Переложение на классическую форму

42:41
  • Определяем команду самым слабым игроком, используя два указателя.
  • Левый указатель берёт следующего по силе игрока, правый указатель двигает до упора.

Аккуратность в реализации

44:06
  • Важно учитывать случаи, когда команда схлопывается до одного человека.
  • Необходимо аккуратно работать с индексами, чтобы избежать выхода за пределы массива.

Слияние отсортированных последовательностей

46:07
  • Задача: слить две отсортированные последовательности в одну.
  • Используем два указателя: один для первой последовательности, другой для второй.
  • Выводим элементы из обеих последовательностей, сравнивая их по указателям.

Реализация слияния последовательностей

47:30
  • Идея слияния последовательностей проста, но реализация сложна из-за множества проверок.
  • Стандартные реализации могут быть длинными, но быстрыми.
  • Предлагаемая реализация короче, но медленнее.

Псевдобесконечность в алгоритме

48:26
  • Создаётся список для ответа и устанавливаются указатели на ноль.
  • Вводится псевдобесконечность для предотвращения ситуаций, когда одна из последовательностей заканчивается.
  • Бесконечность вычисляется как максимальное число из двух последовательностей плюс единица.

Проблемы с текущей реализацией

50:14
  • Код не очень понятен и требует много времени для осмысления.
  • Решение может не работать с неизменяемыми объектами, такими как строки или кортежи.

Более понятная реализация

51:14
  • Выделяется память под две слитые последовательности, устанавливаются указатели на ноль.
  • Проверяется, что первая последовательность не закончилась и либо вторая закончилась, либо нужно взять меньшее число из первой.
  • Если первая последовательность закончилась или во второй последовательности меньшее число, берётся из второй.

Вопросы и ответы

53:06
  • Вопрос о достаточности курса для собеседования: зависит от математической культуры и опыта программирования.
  • Для успешной работы с большими данными необходимы алгоритмы, такие как префиксная сумма.

Префиксная сумма и подсчет отрезков

55:52
  • Объяснение использования единицы в префиксной сумме для корректного подсчета отрезков с суммой ноль.
  • Пример с отрезком «три минус три» показывает, что без единицы в префиксной сумме можно допустить ошибку.

Правила для переменных-счётчиков

56:06
  • Вопрос о общепринятых правилах для названия переменных-счётчиков остаётся открытым.

Названия переменных

57:05
  • Программы автора не являются образцом для именования переменных.
  • Требования к именованию переменных меняются в зависимости от команды.
  • Компромисс между длиной и осмысленностью имён переменных.

Использование break и continue

58:13
  • Break и continue могут нарушать логику программы.
  • Много брейков в цикле могут указывать на проблемы в коде.
  • В учебных задачах использование брейков может замедлить программу.

Решение третьей задачи

59:39
  • Объяснение работы алгоритма за O(n).
  • Левый указатель перебирает все элементы цикла.
  • Правый указатель двигается только вправо, что ограничивает количество действий.

Сортировка данных

1:01:46
  • Данные для задачи про футболистов нужно отсортировать заранее.
  • Стандартная сортировка даёт сложность O(n log n).

Алгоритмы и структуры данных

1:02:14
  • Список алгоритмов доступен на странице курса.
  • Разделяй и властвуй и динамическое программирование не будут изучаться в явном виде.
  • Курс охватывает полезные алгоритмы для широкого круга задач.

Сложность работы словарей

1:04:30
  • Словари работают в среднем за O(1) при равномерном распределении данных.
  • Подробнее о работе словарей можно узнать в лекциях о множествах.