← Назад к курсу

Обучение решению задачи с LeetCode уровня Easy

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

Как самостоятельно найти и решать задачи Easy

Вот эффективная стратегия, составленная на основе рекомендаций опытных разработчиков из найденных материалов:

  1. Начните с проверенных списков: Вместо случайных задач используйте структурированные планы обучения на самом LeetCode. Особенно рекомендуют начать с LeetCode 75 или "Top 100 Liked" задач.
  2. Фильтруйте по сложности и тегам: На странице Problemset установите фильтр Difficulty: Easy. Затем можно выбрать конкретную тему (например, "Array", "String", "Hash Table"), чтобы изучать паттерны.
  3. Сортируйте по Acceptance Rate: Задачи с более высоким процентом успешных решений (Acceptance), как правило, проще и лучше подходят для старта.
  4. Изучайте темы последовательно: Сфокусируйтесь на одной фундаментальной теме за раз (массивы, хеш-таблицы, строки), решите 5-7 задач, чтобы закрепить паттерн, и только потом переходите к следующей.

Примеры задач, паттернов и решений на Python

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

1. Two Sum (Две суммы)

  • Паттерн: Использование хеш-таблицы (словаря) для мгновенного поиска.
  • Суть: Найти в массиве два числа, сумма которых равна заданному целевому числу. Вернуть их индексы.
def two_sum(nums, target):
    """
    :type nums: List[int]
    :type target: int
    :rtype: List[int]
    """
    # Создаём словарь для хранения: число -> его индекс
    num_map = {}
    
    for i, num in enumerate(nums):
        complement = target - num  # Вычисляем, какое число нам нужно для пары
        # Если нужное число уже есть в словаре, пара найдена
        if complement in num_map:
            return [num_map[complement], i]
        # Если нет, сохраняем текущее число и его индекс в словарь
        num_map[num] = i
    # По условию задачи решение всегда существует, поэтому сюда не попадём
    return []

# Пример использования
print(two_sum([2, 7, 11, 15], 9))  # Вывод: [0, 1]
print(two_sum([3, 2, 4], 6))       # Вывод: [1, 2]

Пояснение: Вместо двух вложенных циклов (O(n²)) мы проходим по массиву один раз. Для каждого числа проверяем, не видели ли мы уже число, которое в сумме с текущим даёт target. Это возможно благодаря мгновенному доступу по ключу в словаре (хеш-таблице).

2. Valid Parentheses (Валидные скобки)

  • Паттерн: Использование стека (LIFO - "последним пришёл, первым ушёл").
  • Суть: Проверить, правильно ли в строке расставлены скобки (), [], {}.
def is_valid(s):
    """
    :type s: str
    :rtype: bool
    """
    # Стек для хранения открывающих скобок
    stack = []
    # Сопоставление закрывающих скобок с соответствующими открывающими
    mapping = {')': '(', ']': '[', '}': '{'}
    
    for char in s:
        if char in mapping:  # Если символ - закрывающая скобка
            # Достаём последнюю открывающую скобку из стека (или заглушку, если стек пуст)
            top_element = stack.pop() if stack else '#'
            # Сравниваем её с ожидаемой открывающей скобкой
            if mapping[char] != top_element:
                return False
        else:  # Если символ - открывающая скобка, кладём в стек
            stack.append(char)
    # Строка валидна, если после обработки всех символов стек пуст
    return not stack

# Пример использования
print(is_valid("()[]{}"))  # Вывод: True
print(is_valid("(]"))      # Вывод: False

Пояснение: Алгоритм использует стек для отслеживания порядка скобок. Открывающие скобки помещаются в стек. При встрече закрывающей скобки проверяется, соответствует ли она последней открывающей (вершине стека). Если нет или стек пуст — последовательность невалидна.

3. Best Time to Buy and Sell Stock (Лучшее время для покупки и продажи акций)

  • Паттерн: Один проход (проход с отслеживанием минимума).
  • Суть: Дан массив цен акций. Нужно максимизировать прибыль, купив в один день и продав в другой, более поздний день.
def max_profit(prices):
    """
    :type prices: List[int]
    :rtype: int
    """
    min_price = float('inf')  # Инициализируем минимальную цену как бесконечно большую
    max_profit = 0            # Инициализируем максимальную прибыль как 0
    
    for price in prices:
        # Обновляем минимальную цену, если найдена меньшая
        if price < min_price:
            min_price = price
        # Пробуем продать по текущей цене и обновляем прибыль, если она больше
        potential_profit = price - min_price
        if potential_profit > max_profit:
            max_profit = potential_profit
            
    return max_profit

# Пример использования
print(max_profit([7, 1, 5, 3, 6, 4]))  # Вывод: 5 (купить за 1, продать за 6)

Пояснение: Задача решается за один проход. Мы постоянно отслеживаем самую низкую цену (min_price), встреченную к текущему моменту. Для каждой последующей цены мы вычисляем, какую прибыль можно было бы получить, если бы купили по min_price и продали по текущей цене, и обновляем максимальную прибыль (max_profit).

Ключевые советы для эффективного обучения

  • Не сидите над одной задачей слишком долго: Если не получается найти решение за 30-40 минут, изучите готовое решение, а затем попробуйте написать код заново самостоятельно.
  • Практикуйтесь регулярно: Гораздо эффективнее решать по 1-2 задачи в день, чем 20 задач за выходные.
  • Используйте Python: Этот язык рекомендован из-за лаконичного синтаксиса и богатого набора встроенных структур данных, что позволяет сосредоточиться на логике, а не на особенностях языка.

Для дальнейшего изучения рассмотрите следующие темы и связанные с ними задачи Easy:

  • Два указателя (Two Pointers): Задачи Reverse String, Valid Palindrome.
  • Бинарный поиск (Binary Search): Задача Binary Search.
  • Связные списки (Linked Lists): Задачи Reverse Linked List, Merge Two Sorted Lists.

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