habrahabr

Я решал LeetCode 600 дней подряд и что из этого вышло

  • пятница, 31 октября 2025 г. в 00:00:09
https://habr.com/ru/companies/betboom/articles/959246/
Эволюция программиста
Эволюция программиста

Все знают о LeetCode — его можно любить, ненавидеть, презирать или даже бояться, но равнодушным точно не останется никто.

А для тех, кто все‑таки не знает, LeetCode — платформа для решения алгоритмических задач разной сложности и тематики, соревнований по скорости и производительности и просто общению с коммьюнити единомышленников по этой теме.

Эта статья - впечатления о моём 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 задачек. 

Какую пользу можно извлечь из такой авантюры?

  1. Алгоритмическая сложность - ее можно будет увидеть даже невооруженным взглядом и сразу вычислять недостатки алгоритмов / методов и т.п. В продакшн коде это иногда может дать небольшой буст при некоторых сценариях, и ваши пользователи наверняка скажут вам спасибо.

  2. Изучение языка - возможно, лучше способа для этого и не придумать. Человек - ленивое существо, и вам наверняка не захочется в очередной раз писать шаблонный код для тривиальной задачи, а воспользоваться готовой функцией. Для этого придется подробно ознакомится с 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()
  3. Паттерны - любой базовый алгоритм превратится для вас в набор шаблонных действий разной очередности. Со временем  понимать чужие решения станет в разы проще.

  4. Структуры данных - если это ваша слабая сторона, то LeetCode поможет исправить это: массивы, списки, хэш таблицы, деревья, графы и даже стэк с очередью будут ждать вас в решениях самых разных задач. 

По началу придется смотреть чужие решение — по ним можно понять оптимальные пути решения проблем и увидеть шаблоны.

Возможно, кто‑то возразит о вышеперечисленных плюсах LeetCode. И скорее всего он даже будет в чем‑то прав, ведь универсального подхода к изучение алгоритмов нет: что‑то познается в практике, а что‑то в теории, а кому‑то они просто не нужны.

LeetCode — это просто один из удобных и порой интересных инструментов, где можно это освоить и «посоревноваться» с другими.


Почему не hard

Взглянув на статистику моего профиля, можно сразу увидеть одну немаловажную деталь - я вообще не решаю задачи уровня hard.

Плохо ли это? Опять же все зависит от ваших целей и возможностей.

Если ваша цель - подготовиться к собеседованию в Google или другие подобные компании, то это будет маст хев. Но для базового понимания алгоритмов и игры "в долгую" это явно будет лишним - заканчивая 8-часовой рабочий день, вы вряд ли захотите потратить еще пару часов на решение головоломки уровня hard для достижения эфемерной цели "узнать алгоритмы" Здесь больше подходит правило привычки, а не быстросгорающей мотивации, на которой далеко не уедешь.

Как правило, для решения hard задач, вам понадобится неплохая подготовка, пара часов свободного времени и крепкие нервы, потому что даже работающее решение вам могут забраковать по ограничению памяти или времени, поэтому стоит отталкиваться от вашей цели.


Решения

Из всех решений, что я видел за это время на LeetCode, я выделил следующие группы:

  1. Максимально производительные и правильные решения.

    Они основаны на паттернах или даже математике и дают "100% beats" в ваше решение: std функции языка не используются, а «изобретается велосипед», но весьма производительный. 

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

  2. Элегантные и непроизводительные решения.

    Обилие вызовов функций стандартной библиотеки языка; понятный и лаконичный код, который будет более дружелюбным для неподготовленного читателя.

    Такой код не претендует на эффективность, но демонстрирует красоту языка и уровень его познания у написавшего.

    Именно этот вариант для меня - максимально предпочтительный, ведь больше всего похож на продакшн код в большинстве проектов.

  3. «Чтобы просто работало».

    Немалая доля решений, которые сочетают в себе все что только можно - лишь бы работало. В них авторы редко задумываются о красоте или сложности той или иной вызываемой функции.

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

Для примера - сравним подходы к решению одной и той же задачи из подборки Яндекса

Производительный вариант (~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

Код выглядит абсолютно по-разному, хотя алгоритм практически идентичен.

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

  1. Первое решение заметно выигрывает в скорости.

  2. Для человека, который не знаком с языком, второе решение может быть сложным для понимания.

  3. Их алгоритмическая сложность одинакова и равна O(N) из-за полного обхода.

  4. Первое решение заметно выигрывает по потребляемой памяти (O(1) vs O(n)), из-за отсутствия преобразований структур и работы с исходным массивом.

Дилемма LeetCode'ра
Дилемма LeetCode'ра

На самом деле, существует огромное множество алгоритмов, с помощью которых можно решить почти любую задачу на LeetCode, однако есть ряд самых популярных:

  1. prefix sum

  2. sliding window

  3. hash table

  4. dfs

  5. binary search

  6. two pointer

  7. stack & queue

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

С базовыми интерпретациями можно ознакомиться в прекрасной и легкой книжке - «Грокаем алгоритмы».


Систематичность

В чем же секрет? Да, на самом деле, ни в чем.

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

Это может занять 2 минуты или 2 часа, но задача будет решена и квадратик заполнится зеленым цветом

Единственный минус при таком раскладе -  достаточно часто это просто задача ради задачи: она не учит чему-то новому и уже не развивает какие-то скилы в алгоритмах или языке.

Как я и писал выше, где-то на 200+ день это просто превратилось в рутинную работу, которую я в том или ином виде делаю каждый день.

Если у вас есть конечная цель такого обучения, то скорее всего вы сможете вынести из решения задач больше пользы и видеть более явный прогресс.

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

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


Собеседования

Цель решения этих задачек может быть разной:

  • изучение функций языка;

  • изучение алгоритмов;

  • небольшая разминка для мозга;

  • просто банальная состязательность в решениях между собой;

  • тренировка к прохождению собеседований в бигтех компании по типу Google, Yandex и подобных.

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

Поможет ли вам в этом LeetCode? Скорее да, чем нет. Все зависит от подхода к решению задач.

Основная цель - понять какой-то паттерн решения и получить навык применять его в решении аналогичной задачи

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

Иногда даже сами HR высылают список задач на тренировку к алгоритмической секции собеседования (Yandex).

Исходя из опыта коллег, для порога прохождения собеседований необходимо решить хотя бы 100 задач easy/medium сложности. При этом это не дает никаких гарантий и некоторым может понадобится в разы больше.

Из своего опыта могу сказать, что уверенность в себе начинает появляться примерно после 300+ задач.

Однако, не стоит быть слишком самоуверенным, ведь среди 3-х тысяч задач всегда найдется та, что сможет вдребезги опустить вашу самооценку

Приступ синдрома самозванца
Приступ синдрома самозванца

П.с. картинка взята из чужой статьи про литкод

Также ходят слухи, что некоторые HR могут хантить лучших LeetCode'еров, которые занимают призовые места на турнирах и ведущие места лидербордов.


Советы

И наконец от себя хотел бы дать ряд советов:

  1. Определитесь с целью.

    Правильная цель задаст нужный ритм вашей работе!
    Возможно, оно вам просто не нужно.

  2. Начните с простого.

    Мало кто сможет сходу решить большинство medium и hard задач.
    Обращайте внимание на Acceptance Rate в задачах: чем выше процент, тем больше вероятность решить ее.

  3. Не стесняйтесь смотреть чужие решения.

    В них часто можно найти что-то новое для себя: алгоритмы, удобные функции, радикально новые взгляды.

  4. Экспериментируйте.

    Большинство задач имеют несколько решений.
    Не все из них будут оптимальными, но это все еще верные решения.
    Лучшее - враг хорошего.

  5. Привычки сильнее мотивации.

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

  6. Делитесь своими решениями.

    Если вы пишете на популярном языке, то можете получить одобрение или ряд советов от единомышленников. Это поможет влиться в это сообщество и прокачать свои скиллы.


Итоги

В самом начале, перебарывая себя еще на этапе решения easy задач, я потратил колоссальное количество усилий и времени. И по итогам 600 дней с уверенностью утверждаю - все это было сделано не зря.

LeetCode - это отдельный мир, в котором свои правила игры и не так много соприкосновений с реальностью.

Но он точно даст вам понять, что такое алгоритмы и нужны ли они вам: для работы, собеседований, учебы или общего развития.