- PVSM.RU - https://www.pvsm.ru -
Все знают о LeetCode — его можно любить, ненавидеть, презирать или даже бояться, но равнодушным точно не останется никто.
А для тех, кто все‑таки не знает, LeetCode [1]— платформа для решения алгоритмических задач разной сложности и тематики, соревнований по скорости и производительности и просто общению с коммьюнити единомышленников по этой теме.
Эта статья - впечатления о моём 600-дневном мараф��не на этой платформе, динамике моих скилов и ответе на главный вопрос “надо ли решать там задачи?”.
Как правило, по отношению к LeetCode люди делятся на два лагеря:
ценители и любители LeetCode и алгоритмов;
ненавистники LeetCode, которые считают его бесполезной тратой времени.
При этом длительное время я не принадлежал ни первому, ни второму лагерю, хотя иногда и проявлял интерес в сторону LeetCode, но руки так и не доходили. Страшно было даже притрагиваться к такой фундаментальной вещи, как алгоритмы. Но вот, в июльский вечер 2023 года я все-таки решился посмотреть пару задачек - на этих задачках и закончилось мое первое знакомство с платформой. Интерес был утолен, и на какое-то время я успокоился.
На этом мое знакомство с Leetcode закончилось и на некоторое время отстранился.
Все было спокойно, пока мы с другом не заключили спор - сможем ли мы решить 100 задач до конца 2023 года? А это было 50 задач всего за 1 месяц - декабрь.
На одном из моковых собеседований мы услышали, что для прохождения алгоритмического этапа может хватить решения 50 задач на Литкоде.
Челлендж в 100 задач оказался достаточно легким - Новый год мы встречали уже с круглым числом выполненных задач в профиле. Так быстро мы решили не останавливаться - Покоренная вершина стимулировала покорить новую - 200 задач к началу лета (за 5 месяцев).
В конце челленджа в 200 задач мой друг принял решение сойти с дистанции - переизбыток алгоритмов в крови, голове и остальных частях тела вызывал у него дискомфорт и галлюцинации, поэтому в его профиле красуется круглое «200», а я же к этому времени только “разогрелся” и вошел во вкус.
24 февраля 2024 в течении недели LeetCode предлагал неплохие и не очень сложные задачи на дейли челлендже, и у меня случайно получился стрик в районе 10 дней подряд.
Сбивать стрик было как-то жалко - это же целых 10 дней. Так и началась долгая история в 600 дней...
В итоге стрик продлился на 600+ дней и 700+ задач и продолжается на текущий момент: явный интерес был первые 100-200 дней, а далее началась рутина и тяжелая дисциплинированная работа - важно было установить личный рекорд.
Мой алгоритм в решении был весьма тривиален:
Если дейли решаемый - решаю дейли.
Если чувствую, что на дейли времени нужно больше, чем обычно - решаю любую попавшуюся под руку задачу.
Как правило, под руку попадались Easy/Medium задачи - этого хватало, чтобы размять свою голову с утра / перед сном.
Со временем я стал четче ощущать внутреннюю уверенность перед стартом решения задачи любой сложности - яркий признак того, что время потрачено не зря.
Теперь, видя огромную лесенку из 10+ вызовов std функций языка, больше нет страха, а есть четкая цепочка шаблонных действий.
Главный вопрос:
А надо л�� оно? Для точного ответа на этот вопрос важно ответить на уточняющие:
Вы работаете с высоконагруженной системой?
Хотите ли вы попасть в бигтех?
Вас привлекают алгоритмы и приносят вам радость?
Если хотя бы на один вопрос вы ответили «да», то LeetCode - подходящий способ прокачать ваши алгоритмические скилы. Хотя бы на 100 задачек.
Какую пользу можно извлечь из такой авантюры?
Алгоритмическая сложность - ее можно будет увидеть даже невооруженным взглядом и сразу вычислять недостатки алгоритмов / методов и т.п. В продакшн коде это иногда может дать небольшой буст при некоторых сценариях, и ваши пользователи наверняка скажут вам спасибо.
Изучение языка - возможно, лучше способа для этого и не придумать. Человек - ленивое существо, и вам наверняка не захочется в очередной раз писать шаблонный код для тривиальной задачи, а воспользоваться готовой функцией. Для этого придется подробно ознакомится с std функциями вашего языка. Спустя какое-то время вы сможете писать их даже в блокноте без подсказок ide, что иногда требуют на некоторых собеседованиях.
Junior код:
fun countChars(s: String): Map<Char, Int> {
val res = mutableMapOf<Char, Int>()
for (c in s) {
if (res.contains(c)) res[c] = res[c]!! + 1
else res[c] = 1
}
return res
}
Senior код:
fun countChars(s: String): Map<Char, Int> =
s.groupingBy { it }.eachCount()
Паттерны - любой базовый алгоритм превратится для вас в набор шаблонных действий разной очередности. С�� временем понимать чужие решения станет в разы проще.
Структуры данных - если это ваша слабая сторона, то LeetCode поможет исправить это: массивы, списки, хэш таблицы, деревья, графы и даже стэк с очередью будут ждать вас в решениях самых разных задач.
По началу придется смотреть чужие решение — по ним можно понять оптимальные пути решения проблем и увидеть шаблоны.
Возможно, кто‑то возразит о вышеперечисленных плюсах LeetCode. И скорее всего он даже будет в чем‑то прав, ведь универсального подхода к изучение алгоритмов нет: что‑то познается в практике, а что‑то в теории, а кому‑то они просто не нужны.
LeetCode — это просто один из удобных и порой интересных инструментов, где можно это освоить и «посоревноваться» с другими.
Взглянув на статистику моего профиля, можно сразу увидеть одну немаловажную деталь - я вообще не решаю задачи уровня hard.
Плохо ли это? Опять же все зависит от ваших целей и возможностей.
Если ваша цель - подготовиться к собеседованию в Google или другие подобные компании, то это будет маст хев. Но для базового понимания алгоритмов и игры "в долгую" это явно будет лишним - заканчивая 8-часовой рабочий день, вы вряд ли захотите потратить еще пару часов на решение головоломки уровня hard для достижения эфемерной цели "узнать алгоритмы" Здесь больше подходит правило привычки, а не быстросгорающей мотивации, на которой далеко не уедешь.
Как правило, для решения hard задач, вам понадобится неплохая подготовка, пара часов свободного времени и крепкие нервы, потому что даже работающее решение вам могут забраковать по ограничению памяти или времени, поэтому стоит отталкиваться от вашей цели.
Из всех решений, что я видел за это время на LeetCode, я выделил следующие группы:
Максимально производительные и правильные решения.
Они основаны на паттернах или даже математике и дают "100% beats" в ваше решение: std функции языка не используются, а «изобретается велосипед», но весьма производительный.
Понять такое решение обычно может или только сам автор, или тот, кто и так решил задачу подобным же образом.
Элегантные и непроизводительные решения.
Обилие вызовов функций стандартной библиотеки языка; понятный и лаконичный код, который будет более дружелюбным для неподготовленного читателя.
Такой код не претендует на эффективность, но демонстрирует красоту языка и уровень его познания у написавшего.
Именно этот вариант для меня - максимально предпочтительный, ведь больше всего похож на продакшн код в большинстве проектов.
«Чтобы просто работало».
Немалая доля решений, которые сочетают в себе все что только можно - лишь бы работало. В них авторы редко задумываются о красоте или сложности той или иной вызываемой функции.
Таких решений достаточно много, особенно в непопулярных языках
Для примера - сравним подходы к решению одной и той же задачи из подборки Яндекса [2]
Производительный вариант (~1ms):
fun canPlaceFlowers(flowerbed: IntArray, n: Int): Boolean {
if (n == 0) return true
var remaining = n
var i = 0
while (i < flowerbed.size) {
if (flowerbed[i] == 0) {
val prev = if (i == 0) 0 else flowerbed[i - 1]
val next = if (i == flowerbed.size - 1) 0 else flowerbed[i + 1]
if (prev == 0 && next == 0) {
flowerbed[i] = 0
remaining--
if (remaining == 0) return true
i++
}
}
i++
}
return remaining == 0
}
Лаконичный вариант (~30ms):
fun canPlaceFlowers(flowerbed: IntArray, n: Int): Boolean = flowerbed
.withIndex()
.filter { it.value == 1 }
.map { it.index }
.let { listOf(-2) + it + (flowerbed.count()+1) }
.zipWithNext { i, j -> (j - i - 2) / 2 }
.sum() >= n
Код выглядит абсолютно по-разному, хотя алгоритм практически идентичен.
Тут каждый сам выберет для себя лучшее решение, но разница между ними очевидна и будет видна даже человеку, который никогда не писал код и не понимает его:
Первое решение заметно выигрывает в скорости.
Для человека, который не знаком с языком, второе решение может быть сложным для понимания.
Их алгоритмическая сложность одинакова и равна из-за полного обхода.
Левое решение заметно выигрывает по потребляемой памяти ( vs
), из-за отсутствия преобразований структур и работы с исходным массивом.
На самом деле, существует огромное множество алгоритмов, с помощью которых можно решить почти любую задачу на LeetCode, однако есть ряд самых популярных:
prefix sum
sliding window
hash table
dfs
binary search
two pointer
stack & queue
Останавливаться на них мы не будем, но если вы разберетесь в них, то большая часть задач LeetCode будет вам под силу.
С базовыми интерпретациями можно ознакомиться в прекрасной и легкой книжке - «Грокаем алгоритмы».
В чем же секрет? Да, на самом деле, ни в чем.
Простая систематичность и привычка - неважно, будний день, выходной или праздничный - вечером я сажусь и решаю задачу любой сложности, что мне по душе в тот момент.
Это может занять 2 минуты или 2 часа, но задача будет решена и квадратик заполнится зеленым цветом
Единственный минус при таком раскладе - достаточно часто это просто задача ради задачи: она не учит чему-то новому и уже не развивает какие-то скилы в алгоритмах или языке.
Как я и писал выше, где-то на 200+ день это просто превратилось в рутинную работу, которую я в том или ином виде делаю каждый день.
Если у вас есть конечная цель такого обучения, то скорее всего вы сможете вынести из решения задач больше пользы и видеть более явный прогресс.
Подводя итог моего 600-дневного путешествия, могу сказать, что базовые паттерны в разных их проявлениях становятся видны достаточно быстро, однако достаточно сложные вариации и комбинации нескольких иногда могут вызвать явные затруднения.
Для прохождения на базовые позиции разработчиков, скорее в��его, этого будет достаточно, но выше могут быть трудности.
Цель решения этих задачек может быть разной:
изучение функций языка;
изучение алгоритмов;
небольшая разминка для ;
просто банальная состязательность в решениях между собой;
тренировка к прохождению собеседований в бигтех компании по типу Google, Yandex и подобных.
Последняя как раз и является самой популярной причиной.
Поможет ли вам в этом LeetCode? Скорее да, чем нет. Все зависит от подхода к решению задач.
Основная цель - понять какой-то паттерн решения и получить навык применять его в решении аналогичной задачи
Для такой тренировки отлично может подойти раздел курсов [4], где задачи сгруппированы по принципу их алгоритмов и поможет вам заметить между ними общие черты.
Иногда даже сами HR высылают список задач на тренировку к алгоритмической секции собеседования (Yandex).
Исходя из опыта коллег, для порога прохождения собеседований необходимо решить хотя бы 100 задач easy/medium сложности. При этом это не дает никаких гарантий и некоторым может понадобится в разы больше.
Из своего опыта могу сказать, что уверенность в себе начинает появляться примерно после 300+ задач.
Однако, не стоит быть слишком самоуверенным, ведь среди 3-х тысяч задач всегда найдется та, что сможет вдребезги опустить вашу самооценку
П.с. картинка взята из чужой статьи про литкод [5]
Также ходят слухи, что некоторые HR могут хантить лучших LeetCode'еров, которые занимают призовые места на турнирах и ведущие места лидербордов.
И наконец от себя хотел бы дать ряд советов:
Определитесь с целью.
Правильная цель задаст нужный ритм вашей работе!
Возможно, оно вам просто не нужно.
Начните с простого.
Мало кто сможет сходу решить большинство medium и hard задач.
Обращайте внимание на Acceptance Rate в задачах: чем выше процент, тем больше вероятность решить ее.
Не стесняйтесь смотреть чужие решения.
В них часто можно найти что-то новое для себя: алгоритмы, удобные функции, радикально новые взгляды.
Экспериментируйте.
Большинство задач имеют несколько решений.
Не все из них будут оптимальными, но это все еще верные решения.
Лучшее - враг хорошего.
Привычки сильнее мотивации.
Я бы никогда не дошел до такого количества решенных задач на одной лишь мотивации.
Ее, как правило, хватит не более, чем на месяц.
Делайте по одному небольшому шагу каждый день и через год вы удивитесь проделанному пути.
Делитесь своими решениями.
Если вы пишете на популярном языке, то можете получить одобрение или ряд советов от единомышленников. Это поможет влиться в это сообщество и прокачать свои скиллы.

В самом начале, перебарывая себя еще на этапе решения easy задач, я потратил колоссальное количество усилий и времени. И по итогам 600 дней с уверенностью утверждаю - все это было сделано не зря.
LeetCode - это отдельный мир, в котором свои правила игры и не так много соприкосновений с реальностью.
Но он точно даст вам понять, что такое алгоритмы и нужны ли они вам: для работы, собеседований, учебы или общего развития.
Автор: qveex
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/algoritmy/435035
Ссылки в тексте:
[1] LeetCode : https://leetcode.com/problemset/
[2] одной и той же задачи из подборки Яндекса: https://leetcode.com/problems/can-place-flowers/description/
[3] мозга: http://www.braintools.ru
[4] раздел курсов: https://leetcode.com/explore/learn/
[5] чужой статьи про литкод: https://habr.com/ru/companies/geekfactor/articles/597035/
[6] Источник: https://habr.com/ru/companies/betboom/articles/959246/?utm_source=habrahabr&utm_medium=rss&utm_campaign=959246
Нажмите здесь для печати.