Тренировки по алгоритмам от Яндекса. Лекция 1: «Сложность, тестирование, особые случаи»

YOUTUBE · 16.11.2025 08:17

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

Введение

0:10
  • Михаил Густокашин представляет себя как преподаватель факультета компьютерных наук в Высшей школе экономики.
  • Курс посвящён алгоритмам и структурам данных.
  • Подчёркивается, что курс не связан с задачами собеседований в Яндексе.

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

0:40
  • При устройстве на работу проводятся несколько собеседований, включая секцию по алгоритмам.
  • На алгоритмическом собеседовании проверяют умение писать работающий код, тестировать его и писать эффективный код.

Проверка навыков

3:16
  • На собеседовании дают задачу, похожую на олимпиадную, без дополнительных условий.
  • Необходимо написать решение на бумаге без использования дополнительных средств.

Критерии для стажёров

4:26
  • Для стажёров без опыта работы требуется написать 10 тысяч строк работающего, эффективного и протестированного кода.
  • Важно, чтобы код был разнообразным и содержал меньше копипаста.

Подготовка к собеседованиям

6:06
  • Решение алгоритмических задач помогает набить руку и улучшить навыки.
  • Существуют сайты с задачами для подготовки к собеседованиям, где код автоматически проверяется.

Структура курса

8:37
  • Курс включает 8 лекций и 4 разбора каждой лекции.
  • Практические занятия включают решение задач с автоматической проверкой.
  • Курс предназначен для людей без опыта программирования или с минимальным опытом.

Содержание первой лекции

10:41
  • Первая лекция охватывает сложность алгоритмов, особые случаи и тестирование.
  • Сложность алгоритмов определяется порядком количества действий, которые алгоритм выполняет.

Линейная сложность

11:05
  • Линейная сложность означает, что количество действий алгоритма линейно зависит от размера входных данных.
  • Пример линейной сложности: цикл, который проходит по всем элементам последовательности, независимо от количества элементарных действий.

Оценка сложности алгоритмов

12:21
  • Сложность алгоритма с двумя вложенными циклами оценивается как O(n^2).
  • Существуют другие методы оценки сложности, такие как функции theta и o-малые, но они сложнее в доказательстве.
  • О-большая сложность означает, что алгоритм выполняет не более указанного количества действий.

Сравнение алгоритмов по асимптотической сложности

13:26
  • Асимптотическая сложность оценивается для больших значений параметра.
  • Даже неэффективный алгоритм с O(n^100) может быть лучше, чем O(n^2) для больших n.
  • Пространственная сложность учитывает количество дополнительной памяти, используемой алгоритмом.

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

15:03
  • Задача: найти самый часто встречающийся символ в строке.
  • Решение: перебрать все позиции строки внешним циклом и символы внутренним циклом, подсчитывая количество совпадений.
  • Сложность алгоритма: O(n^2), где n — длина строки.

Реализация на Python

16:48
  • Пример реализации на Python с консольным вводом-выводом.
  • Объяснение работы с строками и переменными в Python.
  • Использование циклов для перебора позиций и символов.

Оптимизация алгоритма

20:37
  • Идея оптимизации: перебирать только уникальные символы, а не позиции.
  • Новая сложность алгоритма: O(k), где k — количество уникальных символов в строке.
  • Применение функции set для создания множества уникальных символов.

Применение оптимизации

23:28
  • Оптимизация ускоряет решение для длинных строк с повторами символов.
  • Пример с «Войной и миром»: старый алгоритм занял бы слишком много времени.
  • Даже после оптимизации решение может быть улучшено дальше.

Введение в словарь

24:57
  • Создаём словарь, где ключ — символ, а значение — количество его вхождений.
  • Если символ встречается впервые, создаём элемент словаря с ключом и значением 0.
  • При каждом вхождении символа увеличиваем его значение на 1.

Реализация кода

25:32
  • Перебираем символы из строки и увеличиваем значение ключа в словаре при совпадении.
  • Если символ уже был в словаре, увеличиваем его значение.
  • Находим максимум среди значений словаря для каждого ключа.

Анализ сложности алгоритма

28:54
  • Сложность алгоритма: O(n + k), где n — длина строки, k — количество символов в словаре.
  • Поскольку k всегда меньше n, можно считать сложность O(n).

Сравнение с другими методами

29:36
  • Метод с множеством требует O(n * k) памяти.
  • Метод со словарем требует O(n) памяти, так как строку можно не хранить.

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

30:45
  • Удаление строки из кода приводит к ошибке при работе с пустой строкой.
  • Неинициализированная переменная вызывает ошибку времени выполнения.

Обработка пустых последовательностей

32:21
  • Пустые последовательности требуют специальной обработки.
  • Для суммы последовательности выводим 0, если последовательность пуста.
  • Для максимума последовательности выводим бесконечность, если последовательность пуста.

Тестирование алгоритмов

35:04
  • Тестируем алгоритм на примерах из условия задачи.
  • Проверяем общие и особые случаи, включая краевые эффекты и пустые последовательности.
  • Создаём дополнительные тесты для проверки работоспособности алгоритма.

Советы по тестированию

38:12
  • Решаем задачу в голове перед написанием кода.
  • Сверяем результаты с примерами из условия задачи.
  • Добавляем дополнительные примеры для проверки различных ситуаций.
  • Запускаем программу и сравниваем результаты с ожидаемыми.

Краевые эффекты и тестирование

39:33
  • Краевые эффекты возникают, когда границы поиска перепутаны или используются неправильные элементы.
  • Важно проверить код после написания, особенно на крайних случаях.
  • Тесты следует продумать до начала написания кода, чтобы избежать забытых случаев.

Покрытие тестами

40:33
  • Необходимо покрыть тестами все ветвления и циклы.
  • Тесты должны проверять поведение программы на пустых последовательностях и последовательностях из одного элемента.

Маленькие тесты

41:09
  • Один тест должен содержать одну ошибку, чтобы легко локализовать проблему.
  • Большие тесты с множеством ошибок усложняют исправление.

Пример с квадратным уравнением

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

Дополнительные случаи

44:18
  • Проверка случая, когда дискриминант меньше нуля.
  • Обработка случая, когда один из коэффициентов равен нулю.
  • Вывод «бесконечное количество решений» при всех нулевых коэффициентах.

Порядок корней

47:44
  • Ошибка в порядке вывода корней при отрицательных коэффициентах.
  • Добавление теста на отрицательный коэффициент для корректного вывода.

Пошаговая отладка

49:27
  • Использование пошаговой отладки для выявления ошибок.
  • Выполнение программы по строкам для точного понимания процесса.
  • Избегание размышлений о коде вместо его выполнения.

Проверка алгоритма

52:56
  • Проверка корректности алгоритма через псевдокод.
  • Выполнение каждой операции в голове для понимания работы алгоритма.

Оптимизация времени исполнения и памяти

53:40
  • На практике чаще оптимизируют время исполнения, а не память.
  • Если алгоритмы имеют одинаковое время исполнения, выбирают тот, который потребляет меньше памяти.
  • Существуют редкие задачи, где память играет решающую роль.

Создание множества и его порядок

54:20
  • Создание множества линейно, но порядок элементов в нём случайный.
  • Отсутствие порядка в множестве влияет только на вывод символов, но не на их частоту.
  • Перебор элементов множества в случайном порядке корректен.

Анализ памяти в решениях

55:50
  • В первом решении нужно хранить всю строку, даже если символы поступают по одному.
  • Во втором решении приходится хранить строку и множество.
  • В третьем решении строка не хранится, что экономит память.

Собеседования и стандартные функции

57:05
  • На алгоритмическом собеседовании могут попросить реализовать функции вручную.
  • На собеседовании по языку уместно использовать знания языка.
  • Важно понимать, куда вы идёте, чтобы правильно подготовиться.

Сложность алгоритмов

58:27
  • Сложность функции, принимающей вектор на 100 тысяч чисел, будет O(100000).
  • Формально это константа, но задача очень большая.
  • Обсуждение сложности рекурсивных алгоритмов будет в следующей лекции.

Использование библиотек

1:00:27
  • В реальных проектах лучше использовать библиотеки, но понимать их внутреннюю работу.
  • Общее представление о работе библиотечных функций полезно.

Обработка исключений

1:01:38
  • Обработка исключений не нужна в алгоритмических задачах.
  • На алгоритмической секции обработка исключений не потребуется.

Поиск ошибок в коде

1:03:06
  • Метод пристального взгляда: выполнение кода по строчкам для поиска ошибок.
  • Стресс-тестирование: создание примитивного решения и сравнение его с сложным решением на случайных тестах.
  • Если ответы не совпадают, найден тест, на котором решение работает неправильно.

Заключение

1:06:38
  • Прощание и приглашение задавать вопросы в чате.