Привет, Хабр!

Сегодня я расскажу вам про мой путь от 0 до 500+ задач на Leetcode. Сначала, пару слов о себе: достаточно слабое образование, завалил кучу собеседований на алгоритмы (например, в Авито где-то в 2020 году), никогда не умел решать задачи, и не любил. Долгое время узнав о секции алгоритмов просто отказывался от собеседований. Сейчас не боюсь и могу. Даже в Бигтех эту секцию проходил несколько раз.

Я делал всякий фронтенд, потом стал делать бекенд (на NodeJS), но алгоритмы никогда не получались.

И вот, засучив рукава, я решил разделаться с этим безобразием раз и навсегда. Мой профиль на LeetCode на сегодняшний день выглядит так:

(при этом я не гонюсь за числом в профиле, но гонюсь за навыком)

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

Кому статья не подойдет: "Мне живется хорошо, задачки ваши не нужны". Не нужны так не нужны :)

Здесь я не буду перечислять, какие задачи бывают, и как решать каждый тип. Лично мне такие статьи не помогали. Я буду описывать подходы, которые помогли мне самому, и до внедрения которых ничего не получалось. 

Шаг 0. Мотивация

Одной из самых больших проблем для меня было найти мотивацию. Я понимал что решение задачек – моя слабая сторона, но не понимал, как это сделает из меня более крутого разработчика. При этом я замечал, что люди с более высокими хардскилами, чем у меня, все-таки, умеют их решать.

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

  Есть целый пласт задач на реализацию алгоритмов и структур данных, которые используются в других местах. Хороший пример – BST (Binary Search Tree). Реализовав его 1 (или пару) раз "руками" начинаешь глубже понимать, как это работает, и когда читаешь про системный дизайн и видишь: "индекс в СУБД реализован при помощи BST" то понимание происходящего приходит намного быстрее и легче. Сюда же бы отнес реализацию LRU (Least Recently Used) Cache, binary search, …

Меньше "магии" в инструментах

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

  То же самое с играми. Поиск кратчайшего пути из точки А в точку Б для вашего персонажа становится куда более понятным с точки зрения реализации.

Спортивный интерес

  Здесь, пожалуй, мое личное, но все же. Это похоже на некоторое состязание для разработчиков. В принципе, можно было выбрать любую интеллектуальную дисциплину. Задачки на алгоритмы объединяют в себе разработчиков приложений/бекенда/фронтенда, и это в какой-то степени наша общая боль 🙂

Гарант дисциплины / универсальное мери́ло

  Я воспринимаю это так: "если уже этот человек смог со всех сил навалиться и преуспеть в одном деле – то сможет и в другом". Можно было бы взять что угодно – шашки/спорт/вышивание… Если человек обладает достаточной упертостью и умеет фокусироваться на цели и достигать ее – он это сможет сделать в рамках вашей компании. Это очень полезное качество. Но если шашки и вышивание были бы плохим мери́лом – то навык решения алгоритмов это вполне себе подходящее место.

Шаг 1. Режим

  Стоит принять, что развитие этого навыка невозможно с наскока. Нельзя навалиться со всех сил и осилить его. Несколько раз я засучив рукава пытался со всех сил взять и прокачаться. Потом я уставал, что-то случалось, смена работы, отпуск, … В итоге я мог осилить ~15 задач за год:

Привычка – вторая натура

  А что если не осилить этот навык за 2 недели? И даже за месяц. А что если попробовать выделять немного времени, но каждый день? Буквально одну помидорку. Можно даже оставить в отпуске и на праздники, но делать что-то совсем простое (~5-10 минут), чтобы все-таки восстанавливаться, но при этом не потерять привычку и не "вкатываться" в режим заново.

  Как работает у меня: Каждое утро я посвящаю примерно 1 помидорку на обучение за утренним кофе. Иногда чуть больше, иногда чуть меньше. В какой-то момент я решил, что прям сейчас алгоритмов достаточно, и начал посвящать эту рутину другим темам (System Design / какая-то технология конкретная / etc).

Индивидуальное позитивное подкрепление

  Тут нужно найти то, что драйвит лично вас. Меня драйвила серия дней без пропусков (эдакий Duolingo-style), сначала это целых 10 дней когда я решал задачи подряд, потом это 30! Целый месяц! Так я докатился до 365 дней решения задач на Leetcode подряд. Если есть друзья, которые вас хвалят – замечательно, делитесь с ними своими продвижениями, получайте похвалу. Главное, найдите то, что вам помогает. 

Мой профиль выдерживая режим:

"Keep grinding". Почему это работает

"Keep grinding" ("просто продолжай делать"). Все знают, что "без труда не выловить и рыбку из пруда", и вообще надо трудиться, но все же. За счет чего именно упорство может привести к успеху?

Типы задач не бесконечны

  Когда начинаешь решать задачи, сталкиваешься с новым классом задач, потом с еще одним, потом с еще одним… И вот проходят недели, а по ощущениям их как будто бы остается бесконечное множество впереди. В этот момент очень важно себе напоминать, что они начнут повторяться. "Keep grinding". И вот как наступит тот момент, когда задачи начнут повторяться – самое сложное позади. На определенном этапе либо с 90% задача будет поддаваться сама, либо с небольшой подсказкой (которые даже на собеседованиях будут).

Что если не решил

  Главная цель – узнать и понять что-то новое сегодня. Если не решили задачу – можно осилить разбор решения, либо запустить debugger на рабочем коде и прям пошагово посмотреть, как выполняется алгоритм. Еще вариант – рисовать на листочке (я прям записывал значения на каждой итерации и визуализировал стрелочками). Самое важное – это понимание. Если сегодня понимания стало больше, чем вчера – вы на верном пути.

Сначала пробуй сам

  Большой соблазн читать решение, и сразу смотреть разбор. "Вот я сейчас много задач разберу, и тогда смогу решать". Здесь очень похоже на изуче��ие языков. Если вы хотите говорить на английском – слушать английский недостаточно, нужно говорить. Поэтому с задачами я рекомендую следующий подход: открываешь задачу, пытаешься решить сам. Если за 5-10 минут не получается вообще ничего – можно идти в решения и разбирать. Если получилось хоть какое-то – хорошо, теперь можно смотреть оптимальное. Если вообще получилось – ура, пожинаем плоды наших трудов.

Восприятие и насмотренность улучшается

  Поначалу уровень когнитивной нагрузки при решении задач очень высок. И это нормально. Достаточно вспомнить момент первой встречи с циклами. Их буквально приходилось запускать в голове и проходить каждую итерацию. Возьмем кусок кода:

for (int i = 0; i < arrLength; i++) {
  sum = sum + arr[i];
}

Сегодня такие блоки кода считываются практически мгновенно, и не расходуются силы на "запуск" каждой итерации цикла в голове (Мозг упрощает знакомый паттерн практически до sum = SUM(arr) и воспринимает почти как обычный текст).

В то время как при первом знакомстве вы разбирали этот код на отдельные команды и воспринимали его примерно так:

int i = 0;

i < arrLength; // Проверяем, где же там arrLength, и какой он

i = 0 + 1; // = 1

sum = sum + arr[0];

i = 1 + 1; // = 2

sum = sum + arr[1]; 

Что отличает блок кода выше от блока кода ниже? Да, объем кода больше, но если написать бинарный поиск несколько раз – он станет требовать куда меньше концентрации внимания:

int left = 0;
int right = nums.size() - 1;
  
while (left <= right) {
  int mid = left + (right - left) / 2;
    
  if (nums[mid] == target) {
    return mid;
  } 

  if (nums[mid] < target) {
    left = mid + 1;
  } else {
    right = mid - 1;
  }
}

Подобная "насмотренность" приходит по всем классам задач, и если вы напишете их по нескольку раз – больше не потребуется тратить на них много сил. И эта "насмотренность" приходит со временем. Ровно так же, как она пришла с for() {}.

Тратить деньги – тоже инструмент

  Когда вы платите за изучение языка вы трудитесь более усердно, чем на бесплатных курсах. Когда вы покупаете абонемент в спортзал, вы посещаете его чаще, чем бесплатный турник на улице. Этот же принцип работает и с обучением алгоритмам. Я купил подписку, получил онлайн дебаггер (очень удобно), и доступ к разбору задач. За рекламу мне, к сожалению, не платят. Этот совет чисто от себя. Но ведь действительно ли вы готовы в это вкладываться, если не готовы потратиться? Вопрос хороший. 

  Покупка платной подписки/книги/курса говорит вам же о том, что вы вкладываетесь в это. И вам становится в том числе жалко расходовать ресурс в пустую. В данном случае ресурс – это не только деньги, но и драгоценное время.

  Лично мой опыт – после покупки платных подписок и покупки книг я точно стал вкладываться больше и находить для обучения больше времени.

Быть лучше, чем вчера

  Если быть откровенным – я не чувствую, что я прям круто решаю задачи. Я ждал, что снизойдет на меня вот это ощущение "ну все, теперь я освоил этот навык раз и навсегда!". Но из объективных вещей – я действительно стал проходить эти собеседования, и я стал ощущать себя намного увереннее по обе стороны технических собеседований. Если каждый день быть лучше чем вчера – однажды станешь достаточно хорош. Даже если ты этого пока не чувствуешь :-)