Как правильно решать задачи на LeetCode: подробный гайд по тренажёру для программистов
Нина Торгунакова из «Злых марсиан» за год нащёлкала 400 алгоритмических задач и рассказала, как извлечь из этого занятия максимальную пользу.
Фото: Getty Images
Нина Торгунакова
Фронтенд-разработчик в «Злых марсианах», любит спорт и pole dance, ведёт канал strip_programmer. КМС по пулевой стрельбе, переводит статьи о разработке, LeetCode warrior.
LeetCode — это онлайн-платформа с алгоритмическими задачами по программированию, своего рода тренажёр для подготовки к техническим собеседованиям. Считается, что если регулярно с ним заниматься, то через 6–12 месяцев ваши шансы устроиться в какой-нибудь бигтех по типу «Яндекса» заметно вырастут.
Тем не менее большинство пришедших на платформу забрасывают занятия уже через неделю — подобная статистика есть, например, на ежегодном Advent of Code. Я же решала задачи на LeetCode больше года — получила неимоверное удовольствие, прокачала важные навыки. Вот как это было.
Зачем мне понадобился LeetCode
Идея попробовать LeetCode время от времени появлялась у меня ещё с университета: мне всегда нравились алгоритмы. Но в полноценное, осознанное желание она переросла осенью 2021 года. Для решения одной из рабочих задач потребовались операции с хеш-таблицами, и я поняла, что слишком долго думаю, как обработать структуру данных. Следовательно, решила я, в моих познаниях есть определённые пробелы, которые нужно восполнить.
С таким настроем я и приступила к челленджу: в течение целого года практически ежедневно разбирала минимум по одному примеру из LeetCode. С тех пор их количество перевалило за четыре сотни.
Я хотела, чтобы это вошло в привычку, так что правило «один день — одна задачка» старалась соблюдать неукоснительно. Если вдруг по каким-то причинам сделать это не получалось — всегда навёрстывала отставание в следующие дни.
Постепенно втянулась, поняла, что пользы от LeetCode гораздо больше, чем я изначально предполагала, и даже описала свой опыт в треде в Twitter, на который меня вдохновили коллеги из «Злых марсиан» и особенно Андрей Ситник. В частности, я попыталась развенчать расхожий миф, что такие задачки годятся только для подготовки к собеседованию.
На самом деле это лишь вершина айсберга. Подобная практика в первую очередь тренирует скорость, с которой вы соображаете, когда пишете код. А ещё позволяет правильно формулировать уточняющие вопросы заказчикам и коллегам. Мне кажется, многим из нас не хватает этого навыка.
В IT-среде часто ругают LeetCode: мол, всё равно в реальной работе кейсы оттуда не встречаются, само по себе знание алгоритмов не помогает писать хороший код, а незнание — плохой и так далее.
Но ведь LeetCode — это просто инструмент: он не плохой и не хороший, не полезный и не вредный. Всё зависит от того, как вы будете его использовать.
Как работать с LeetCode
Первым делом советую заглянуть в Beginner’s Guide в разделе Explore — там подробно объясняется, как всё устроено на сайте и с чего лучше всего начать.
У каждой задачи есть свой уровень: лёгкий, средний или сложный. Чтобы правильно подобрать уровень по себе, нужно понимать, в каких темах вы более-менее сильны, а в каких совсем плаваете.
Лично у меня поначалу знаний было немного, пробелы — практически везде, поэтому по всем темам я выбирала самые лёгкие задания. Наращивала сложность постепенно. Начинала с базовых вещей — строки, массивы — и потихоньку двигалась к более сложным: деревьям, динамическому программированию и так далее.
Чтобы увеличить сложность решаемой задачи, полезно не только повышать уровень (например, с easy переходить на medium), но и понижать acceptance задачи, то есть процент её принятия. Он позволяет понять соотношение правильных решений ко всем отправленным.
В некоторых случаях этот параметр даже более показателен, чем уровень сложности. Действительно, некоторые формально «средние» задания на практике может одолеть меньше пользователей, чем формально «хардовые».
В разделе Explore можно найти также курс по каждой теме, внутри которого уровень задач нарастает постепенно. Как только я открыла для себя эту фичу, стала придерживаться именно такого порядка.
Но после создания аккаунта на LeetCode можно остаться и в разделе All. В нём над кнопками All topics, Algorithms, Database есть хештеги, их можно развернуть и посмотреть количество задач в каждом из них.
Необязательно хватать сразу весь топик. Лучше отфильтровать задачи по хештегам, например Sorting или String, и затем отсортировать по возрастанию сложности.
Ориентируйтесь на собственную интуицию: если вы почувствовали, что задачки даются слишком легко, смело повышайте сложность. И наоборот: если пошло со скрипом — значит, вы слегка переоценили свои возможности и лучше откатиться на предыдущий уровень или более высокий процент acceptance.
Тем, у кого есть премиум-аккаунт, по каждой теме доступны ещё и списки задач, которые чаще всего дают на собеседованиях. У меня был обычный аккаунт, и мне всего хватало для обучения. К тому же в разделах Explore есть много полезных и популярных на собеседованиях задач.
Вы также можете формировать подборки самостоятельно — например, чтобы добавить любимые задачи в избранные или отложить до лучших времён то, что сейчас решить не получилось.
А в разделе All очень просто отыскать самые лёгкие задачи для старта. Там есть удобная сортировка: сначала по сложности, потом по acceptance. Всё, что окажется вверху, — это и есть начальный уровень.
Даже у опытных разработчиков, которых базовыми задачами не удивить, скорее всего, найдутся слабые места. Например, для некоторых теория игр или двусвязные списки — тёмный лес. В таком случае на LeetCode можно найти задачи по этой теме и закрыть существующие пробелы.
Рекомендую идти по порядку, начиная с простых задач. Сперва смотрите объяснения — что это за структура или концепция, на чём она основана. Прочтите материалы, а потом начинайте решать. Когда справитесь с первой лёгкой задачей, можете считать, что примерно поняли принцип, по которому надо работать с такими структурами. Но не останавливайтесь на этом, обязательно повышайте уровень сложности.
Если решаете все лёгкие задачи и большую часть средних, то можете спокойно переходить к другой теме. Останавливаться на сложных задачах необязательно. Они могут потребовать много времени и концентрации, но, по статистике, на собеседованиях их попадётся не больше 20%. Даже продвинутые компании обычно берут задачи medium-уровня — про это хорошо написано на сайте Leetcode Therapy.
Я не считаю, что уровень hard бесполезен. Но для базового понимания разных структур и алгоритмов среднего будет достаточно. Если захочется углубиться, то задачи со звёздочкой можно будет порешать позже.
Как не надо решать задачи
Самая типичная ошибка: многие сразу начинают решать так называемые задачи дня, Daily LeetCoding Challenge. Такая задача всегда закреплена первой в общей таблице. Но брать их я рекомендую, только если ваша алгоритмическая подготовка уже на очень хорошем уровне. Я сама клюнула на удочку после нескольких месяцев тренировок — и это был самый непродуктивный период за всё время обучения. Иногда таски попадались настолько сложные, что их решение затягивалось на часы, а это совершенно непродуктивно.
Дело в том, что Daily LeetCoding Challenge, как и следует из названия, — скорее состязание, чем обучение. Поэтому решать daily-задачи без хорошего бэкграунда вредно для психики и скорее только приведёт к выгоранию. Оно вам надо?
Ещё больший мазохизм для новичка — перейти в раздел Contest, где вам отведут час на три задачи. Причём соревноваться с вами будут профессиональные спортивные программисты из Китая, которые за одну минуту решают что угодно. Это самый верный способ потерять веру в себя, поэтому идти туда нужно очень подготовленным.
Как надо решать задачи
Лайфхак №1: при чтении условия сразу думайте о граничных случаях. Проанализируйте, как ограничения, перечисленные в задаче, могут повлиять на алгоритм. Переберите в уме подходы, с помощью которых её можно решить. Так победите.
Лайфхак №2: долго сидеть над одной задачей — контрпродуктивно. Работа с LeetCode не предполагает больших и чересчур сложных решений. Это скорее маленькие таски, рассчитанные на максимум 100 строк, даже если вы пишете код на С++. Поэтому рекомендую посвящать LeetCode не больше 45–60 минут в день.
Лайфхак №3: если вы долго не можете нащупать решение — скорее всего, вы движетесь в неверном направлении. Это не страшно, я тоже иногда не могу что-то решить в течение получаса. В таком случае я перехожу в раздел Discuss и читаю, что пишут люди, которые работают на моём языке программирования. Почему задача может не решаться быстро? Проблем обычно бывает несколько.
Вы действительно не знаете решение и пошли по ложному пути. Тогда задачу лучше отложить и понять, что конкретно вы не знаете.
Совет: выпишите вопросы в план, почитайте статьи и нужные главы в учебнике или в разделе Explore. Затем, спустя некоторое время, вернитесь к задаче.
Вы относитесь к LeetCode как к соревнованию или генеральной репетиции собеседования. Но если интервью с лайвкодингом не ждёт вас буквально через неделю, проще смотреть на LeetCode как на обычный обучающий сервис. Задачи нужны вам для того, чтобы понять, в каких темах вы плаваете, почитать об этом, разобраться в деталях и вернуться для закрепления.
Совет: на LeetCode есть раздел «Подборки», куда можно добавить задачу, чтобы она не потерялась, и попытаться решить её позже. Соответствующая кнопка называется My List и отмечена в меню профиля сердечком или папкой со звёздочкой.
В какой-нибудь строчке спряталась глупая ошибка, или одно действие не пришло в голову. Часто это становится понятно после просмотра готового решения. В этом случае можно исправить своё решение или добавить в него недостающий фрагмент и обдумать, почему вы этого не сделали сразу.
Совет: решения многих задач на сайте можно посмотреть даже без премиум-подписки. Подглядывать в ответы полезно — это нормальный процесс обучения. Если вам немного не хватило знаний, в этом нет ничего зазорного, главное — знать свои слабые места.
Вы берёте задачи хаотично. Очень легко выгореть, если заниматься слишком много и особенно если пытаться решить подряд несколько слишком сложных задач. Проблема обычно в том, что вы не идёте по пути постепенного возрастания сложности.
Совет: возможно, у вас не скорректирован план обучения и вы хватаетесь за задачи из разных тем. В этом случае у вас не упорядочиваются знания по определённой теме, а рост происходит вразнобой. Двигаясь так, очень легко потерять мотивацию и всё возненавидеть.
Вы начали с daily-задач. Некоторые переоценивают свои знания и сразу берутся за сложное. В общем, смотри раздел выше.
Совет: если всё-таки набрались смелости и решили попробовать свои силы в Daily, кликните на нужном дне в календаре справа в разделе All.
Как фиксировать свой прогресс
При решении задач желательно сразу писать код так, чтобы потом его было не стыдно показать другому программисту, — а ещё лучше действительно его показывать, выкладывая в раздел Discuss с объяснением.
Ваша оценка прогресса, скорее всего, будет очень нестабильна. Может случиться так, что, прорешав много лёгких задач на одну тему, вы переходите на средний или даже высокий уровень и всё получается. А в другой день попадается задача, усложнённая вопросом из незнакомой области, вы не можете её решить и расстраиваетесь. Или, переходя к другой теме и столкнувшись с трудностями, теряете веру в себя.
Если вы плохо ориентируетесь в какой-то теме и каждый раз допускаете ошибки, имеет смысл ей заниматься. Были разделы, в которых, даже если я один раз что-то одолела, решить аналогичную задачу уже не получалось. Например, самый тяжёлый топик для меня — динамическое программирование. Это не просто какой-то чёткий алгоритм или структура данных, подчиняющаяся правилам, это концепция, и нужно не только понимать и знать её, но и видеть, когда и как её можно применить в задачах.
Но всё равно: чем больше задач вы решаете и чем регулярнее это делаете, тем ощутимее растёт уровень знаний, ваша уверенность в себе и скорость, с которой вы пишете правильный код.
Если качество — это плавающий показатель, то рост скорости, с которой первично разбираешься в проблеме, можно отследить уже через пару месяцев. А если это простая тема вроде массивов, то ещё раньше, уже через пару недель.
Мне нравится, что LeetCode своеобразно поощряет постоянно решать задачи: монетками за «дейлики» или просто красивой статистикой в профиле. Конечно, такой мотивации вряд ли хватит, чтобы ломать голову, не сдаваясь ни на день, но это прекрасно работает как дополнительный стимул.
Один из моих знакомых сейчас пишет тред «365 дней с LeetCode», где каждый день делится прогрессом и рассказывает, какие задачи он решил. Спустя одну неделю после начала он подвёл итог: 13 задач. Уже на этом этапе он почувствовал, какие темы стал быстро решать сразу, а для каких надо подтянуть скиллы. И он в этом не одинок: мне стали чаще писать и делиться своим прогрессом люди, которые раньше совсем не решали задачи, но сейчас смогли начать. Кстати, рассказывать о своём успехе друзьям или просто читателям в соцсетях полезно: это помогает не сбиться с пути и найти новых единомышленников.
Как организовать своё время
По отклику людей на мой тред я поняла, что читателей больше всего интересует, как организовать время и энергию, чтобы хватало сил каждый день решать задачи. Хочу поделиться опытом.
Учитывайте уровень своей энергии в разное время суток
Например, как каждая уважающая себя сова, я с утра совсем не могу делать физические упражнения, но вечером с удовольствием занимаюсь спортом. А вот задачи мне легче всего решать на рассвете.
У меня был такой распорядок дня: проснувшись, я решала маленькие задачи на LeetCode, а потом ехала на работу. Или, если утром не было времени, делала их в обеденный перерыв. Уже в конце рабочего дня я полностью отдыхала от умственной деятельности на тренировке.
Я считаю, что такой подход помог не выгореть. Если бы я отработала целый день и решала задачи уставшей, мне было бы сложно не сдаться.
Нужно исходить из привычного вам ритма — только так получится сделать занятия регулярными. Это один из главных советов по самоорганизации.
Не тратьте слишком много времени на задачу
Не все задачи решаются с первого захода. Если решение не приходит в голову в течение 30–40 минут, почитайте дополнительную литературу и посмотрите чужие решения. Затем возвращайтесь к задаче.
Нет ничего зазорного в том, чтобы учиться у других и повторять их удачные приёмы. Это не поражение, а естественная часть обучения.
Помните: решение задач — это марафон, а не спринт
Прокачка знаний с помощью решения задач должно стать вашей долговременной целью. Если вы будете в течение недели решать по 20 задач в день, можно быстро перегореть и остановиться, а через месяц от приобретённых навыков ничего не останется. Поэтому регулярность очень важна. Не нужно набрасываться на задачи и пытаться решить всё, лучше выстроить стабильный процесс.
Итог: какую пользу приносит работа с LeetCode
Часто люди приходят на LeetCode, чтобы подготовиться к собеседованиям. Вы будете смеяться, но конкретно мне мой годовой опыт не пригодился: в последний раз, когда я искала новую работу, я проходила только через одно техническое собеседование (кстати, оно было к «Злым марсианам»), и оно было вообще не про решение алгоритмических задач в онлайн-формате. Тем не менее проведённый на LeetCode год помог мне почувствовать себя увереннее и пройти отбор. И вот почему:
- Это расширило мой кругозор. Погуглить алгоритм и затем внедрить его — зачастую очень быстрый процесс. А вот понять, какой выбрать эффективный путь для решения задачи, — уже интереснее. Я уверена, что в реальной работе нам едва ли требуется полностью по памяти писать HeapSort, но иметь представление, что это и для чего, может быть полезно.
- Научилась писать код более быстро и осознанно за счёт понимания сложности алгоритмов и разных структур данных, способности просчитывать граничные случаи и составлять сценарии для тестирования кода. Вспомните о ситуациях, когда вам попалась маленькая лёгкая задачка, но вы потратили на неё больше времени, потому что не довели до автоматизма написание нужных конструкций. У меня ощутимо выросла скорость работы на языке, на котором я решала всё это время, — на нём я и работаю. Какие-то вещи пишу на автомате и не боюсь задач, которые включают в себя такие структуры, как деревья, например.
- Стала легче учиться новому. Я решала задачи на давно знакомом мне языке. Но знаю, что многие так практикуют язык, который ещё не знают. Это помогает в обучении, потому что, прежде чем подойти к рабочим задачам, как правило, нужно сориентироваться в синтаксисе. Тренировочные задачи помогают понять, когда вы можете уверенно решать что-то небольшое. Это важный этап перед тем, как перейти к серьёзным рабочим процессам.
Раньше я не всегда задумывалась о том, когда код может упасть, то есть каким может быть граничное тестовое значение, при котором что-то сломается или посчитается неправильно.
LeetCode даёт навык сначала думать обо всех этих случаях и только потом посылать ответ. Когда решение высвечивается красным из-за того, что что-то сломалось на граничном случае, постепенно приходишь к мысли, что этот случай надо просчитывать. Это довольно весомый плюс, потому что в работе сильно снижается количество багов. - Научилась задавать правильные вопросы. Некоторые задачи на LeetCode сформулированы хорошо, подробно — сразу можешь прочесть, понять и решить. Другие сформулированы кратко, и только тестовые кейсы помогут понять, что имелось в виду. А иногда даже они не особо помогают. В процессе решения у вас возникают вопросы: может ли, например, быть такой формат входных данных. По-хорошему, это должно быть указано в задаче, но иногда нужно идти в раздел Discuss или дополнительно искать информацию о каком-то термине. И это тоже важный этап в процессе обучения.
- Научилась писать красивый код и объяснять свои решения на английском. Всё это благодаря разделу Discuss. Там есть отличные примеры того, как умные люди пишут и объясняют свои решения на разных языках программирования. Можно многому научиться и взять себе на заметку. Но иногда мне не хватало объяснений, и тогда я выкладывала собственные. При этом старалась сделать код чистым, а пояснения к нему — понятными. Приятно получать за это звёздочки от других людей.
- Стала быстрее схватывать суть задачи, лучше объяснять собственный код, легче находить оптимальное решение. Теперь, когда я вижу ТЗ, быстрее распознаю граничные кейсы, когда что-то может сломаться, или вникаю в смысл задачи глубже — чтобы эффективнее найти способ её решения. Это отметили в «Злых марсианах», когда я проходила тестовое задание. Разработчики часто недооценивают важность софт-скиллов, но при правильном подходе LeetCode может помочь и в этом.
Больше интересного про код в нашем телеграм-канале. Подписывайтесь!