0Pricing
DSA Interview Prep · Урок

Пробное собеседование с ограничением времени: простые и средние задачи

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

«Пробное собеседование с ограничением времени: простые и средние задачи» — бесплатный урок DSA Interview Prep на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения DSA Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс DSA Interview Prep содержит 4 уроков всего.

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

Этот урок имитирует реальное собеседование по программированию. Для каждой задачи Вам следует: (1) прочитать её один раз, (2) определить паттерн за 60 секунд, (3) озвучить свой подход и его сложность, (4) написать решение и (5) проверить его на примерах. Установите таймер. Простая задача должна занимать 10–15 минут, а задача средней сложности — 20–25 минут.

Не заглядывайте вперёд в решение — иначе цель упражнения теряется. Если через 5 минут Вы зашли в тупик, перечитайте условие и найдите ключевое слово, раскрывающее паттерн (отсортированное? минимальное? все сочетания? подмассив?). Способность самостоятельно выйти из тупика так же важна, как и способность быстро решать задачи.

# Mock interview timer simulation
import time

class InterviewTimer:
    def __init__(self, total_minutes):
        self.total = total_minutes * 60
        self.start = None

    def begin(self, problem_name):
        self.start = time.time()
        print(f'TIMER STARTED: {problem_name}')
        print(f'You have {self.total//60} minutes. Go!')

    def checkpoint(self, label):
        if self.start:
            elapsed = time.time() - self.start
            remaining = self.total - elapsed
            print(f'[{label}] Elapsed: {elapsed:.0f}s, Remaining: {remaining:.0f}s')

# Usage in real practice:
timer = InterviewTimer(15)  # 15-minute easy problem
timer.begin('Two Sum')
time.sleep(1)
timer.checkpoint('Identified pattern')

Простая задача 1: правильная расстановка скобок

Задача: Дана строка, содержащая только '(', ')', '{', '}', '[', ']'. Определите, является ли входная строка корректной. Строка корректна, если каждая открывающая скобка закрывается скобкой того же типа в правильном порядке.

Признак: совпадающие пары, важен порядок, последняя открытая скобка должна закрываться первой → Стек. Помещайте открывающие скобки в стек; извлекайте их с помощью pop и проверяйте при закрывающих скобках. Если стек пуст, когда мы пытаемся выполнить pop, или в конце в нём остались элементы, строка некорректна. Сложность по времени O(n), по памяти O(n).

def is_valid(s):
    stack = []
    matching = {')': '(', '}': '{', ']': '['}

    for char in s:
        if char in '({[':
            stack.append(char)
        else:
            if not stack or stack[-1] != matching[char]:
                return False
            stack.pop()
    return len(stack) == 0

# Test cases
test_cases = [
    ('()', True),
    ('()[]{}'  , True),
    ('(]', False),
    ('([)]', False),
    ('{[]}', True),
    ('', True),        # empty string is valid
    ('(((', False),    # unmatched opens
    (')]', False),     # close without open
]
for s, expected in test_cases:
    result = is_valid(s)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: is_valid({repr(s)}) = {result} (expected {expected})')

Простая задача 2: лучшее время для покупки и продажи акций

Задача: Дан массив prices, где prices[i] — цена акции в день i. Найдите максимальную прибыль от одной покупки и одной продажи (сначала нужно купить, затем продать). Верните 0, если получить прибыль невозможно.

Признак: максимальная разница, где левый элемент должен находиться раньше правого → Отслеживайте текущий минимум при просмотре массива слева направо. В каждый день потенциальная прибыль равна current_price - min_so_far. Обновляйте максимальную прибыль. Это решение имеет сложность O(n)/O(1) и является частным случаем алгоритма Кадане.

def max_profit(prices):
    if not prices:
        return 0
    min_price = float('inf')
    max_profit = 0

    for price in prices:
        if price < min_price:
            min_price = price
        elif price - min_price > max_profit:
            max_profit = price - min_price
    return max_profit

# Test cases
test_cases = [
    ([7, 1, 5, 3, 6, 4], 5),   # buy at 1, sell at 6
    ([7, 6, 4, 3, 1], 0),      # monotonically decreasing: no profit
    ([2, 4, 1], 2),             # buy at 2, sell at 4
    ([1], 0),                   # single price: no transaction possible
    ([3, 3, 3], 0),             # flat: no profit
]
for prices, expected in test_cases:
    result = max_profit(prices)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: max_profit({prices}) = {result} (expected {expected})')

Средняя задача 1: сумма трёх чисел

Задача: Дан массив. Найдите все уникальные тройки элементов, сумма которых равна нулю. Решение не должно содержать повторяющихся троек.

Паттерн: два указателя, расширенные до трёх элементов. Отсортируйте массив. Для каждого элемента nums[i] используйте два указателя left = i+1, right = n-1, чтобы найти пары с суммой -nums[i]. Пропускайте дубликаты, переходя через одинаковые значения. Сложность по времени O(n²), по памяти O(1), не считая результата. Сортировка упрощает обработку дубликатов.

def three_sum(nums):
    nums.sort()
    result = []
    n = len(nums)

    for i in range(n - 2):
        # Skip duplicate values for the first element
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total == 0:
                result.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left] == nums[left + 1]:
                    left += 1      # skip duplicate lefts
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1     # skip duplicate rights
                left += 1; right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1
    return result

print(three_sum([-1, 0, 1, 2, -1, -4]))  # [[-1,-1,2],[-1,0,1]]
print(three_sum([0, 0, 0, 0]))            # [[0,0,0]]
print(three_sum([]))                       # []
print(three_sum([1, 2, -2, -1]))           # []

Задача средней сложности 2: самая длинная подстрока без повторяющихся символов

Задача: Дана строка. Найдите длину самой длинной подстроки без повторяющихся символов.

Шаблон: Скользящее окно с множеством (или словарём последних позиций). Поддерживайте окно [левая граница, правая граница]. Используйте expand для перемещения правой границы и добавления каждого символа. Если символ повторяется (то есть уже находится в окне), сдвигайте левую границу, пока дубликат не будет удалён. Отслеживайте максимальный размер окна. Временная сложность O(n), пространственная сложность O(min(n, размер алфавита)).

def length_of_longest_substring(s):
    char_index = {}    # character -> last seen index
    left = 0
    max_len = 0

    for right, char in enumerate(s):
        if char in char_index and char_index[char] >= left:
            left = char_index[char] + 1  # shrink window past duplicate
        char_index[char] = right
        max_len = max(max_len, right - left + 1)
    return max_len

# Test cases
test_cases = [
    ('abcabcbb', 3),   # 'abc'
    ('bbbbb', 1),       # 'b'
    ('pwwkew', 3),      # 'wke'
    ('', 0),            # empty string
    ('au', 2),          # full string
    ('dvdf', 3),        # 'vdf' (skip the first d)
]
for s, expected in test_cases:
    result = length_of_longest_substring(s)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: len_longest({repr(s)}) = {result} (expected {expected})')

Задача средней сложности 3: размен монет

Задача: Даны номиналы монет и целевая сумма. Найдите минимальное количество монет, необходимое для получения этой суммы. Если это невозможно, верните -1.

Шаблон: Классический одномерный DP (вариант задачи о рюкзаке с неограниченным количеством предметов). dp[i] = минимальное количество монет для суммы i. Инициализируйте dp[0] = 0, а все остальные значения — бесконечностью. Для каждой суммы от 1 до целевой переберите все номиналы монет. Для каждой подходящей монеты вычисляйте dp[i] = min(dp[i], dp[i - coin] + 1). Временная сложность O(сумма × количество номиналов), пространственная сложность O(сумма).

def coin_change(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0   # 0 coins to make amount 0

    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i and dp[i - coin] + 1 < dp[i]:
                dp[i] = dp[i - coin] + 1

    return dp[amount] if dp[amount] != float('inf') else -1

# Test cases
test_cases = [
    ([1, 5, 11], 15, 3),      # 11+1+1+1+1... wait: 11+1+1+1+1=5 coins? No: 5+5+5=3
    ([2], 3, -1),              # impossible (only even coins)
    ([1], 0, 0),               # 0 coins for amount 0
    ([1, 2, 5], 11, 3),        # 5+5+1
    ([186, 419, 83, 408], 6249, 20),  # stress test
]
for coins, amount, expected in test_cases:
    result = coin_change(coins, amount)
    status = 'PASS' if result == expected else 'FAIL'
    print(f'{status}: coin_change({coins}, {amount}) = {result} (expected {expected})')

Рабочий процесс решения задач в условиях нехватки времени

Когда время заканчивается, расставляйте приоритеты в таком порядке: (1) рабочее решение методом полного перебора с правильным результатом важнее незавершённого оптимального решения, (2) явно обрабатывайте граничные случаи, (3) пишите чистый и понятный код, а не хитрые однострочные конструкции. Интервьюеры предпочтут аккуратное решение с O(n²), которое проходит все тестовые случаи, решению с O(n), содержащему трудно заметную ошибку.

Если вы поняли, что ваше решение с O(n²) неверно, не бросайте его на полпути: завершите его, протестируйте, а затем предложите оптимизировать, если останется время. Наполовину написанное оптимальное решение принесёт меньше баллов, чем полное, но неоптимальное.

# Priority order when time runs out
priority = [
    ('First priority',  'Correct brute-force that passes all test cases'),
    ('Second priority', 'Optimal solution with bugs is WORSE than suboptimal correct'),
    ('Third priority',  'Edge cases handled visibly (empty input, single element, negatives)'),
    ('Fourth priority', 'Clean variable names and readable code'),
    ('Fifth priority',  'Add complexity statement as a comment at the top'),
]
print('Under time pressure, prioritise:')
for priority_level, desc in priority:
    print(f'  {priority_level}: {desc}')

# Adding complexity as a comment
def two_sum_commented(nums, target):
    # Time: O(n), Space: O(n)
    seen = {}
    for i, n in enumerate(nums):
        complement = target - n
        if complement in seen:
            return [seen[complement], i]
        seen[n] = i
    return []

Проверка решения: пять вопросов

Прежде чем сказать «Я закончил», задайте себе эти пять вопросов:

  1. Обрабатывает ли решение пустые входные данные? [], '', None, n=0
  2. Обрабатывает ли решение единственный элемент? Массивы размера 1, деревья с одним узлом
  3. Обрабатывает ли решение одинаковые элементы? [5, 5, 5, 5], 'aaaa'
  4. Обрабатывает ли решение минимальные и максимальные значения? Отрицательные числа, очень большие целые числа, 0
  5. Указали ли вы временную и пространственную сложность? Нотация O с кратким обоснованием

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

# The five edge-case categories with examples
edge_cases = {
    'Empty input':     ['[] empty array', '"" empty string', 'None / null'],
    'Single element':  ['[42]', 'single node tree', 'n=1'],
    'All same':        ['[3,3,3,3]', '"aaaa"', 'uniform grid'],
    'Extreme values':  ['[-10^9, 10^9]', 'INT_MAX + 1 overflow check', '0 as input'],
    'Already sorted':  ['ascending + descending', 'already optimal input'],
}
for category, examples in edge_cases.items():
    print(f'{category}:')
    for ex in examples:
        print(f'  - {ex}')
    print()

# Template for self-testing:
def test_my_solution(fn, test_cases):
    for inputs, expected in test_cases:
        result = fn(*inputs) if isinstance(inputs, tuple) else fn(inputs)
        status = 'PASS' if result == expected else 'FAIL'
        print(f'{status}: {inputs} => {result} (expected {expected})')

Работа с дополнительными вопросами

После решения задачи интервьюеры обычно задают дополнительные вопросы. Распространённые варианты:

  • «Можете ли вы решить её с пространственной сложностью O(1)?» → Поищите изменение входных данных на месте или математические приёмы
  • «Что, если n очень велико?» → Обсудите потоковую обработку, разбиение на страницы или выборку
  • «Что, если массив уже отсортирован?» → Часто существует более простой алгоритм
  • «Можете ли вы распараллелить это?» → Выделите независимые подзадачи и обсудите MapReduce или параллельное выполнение задач

Дополнительные вопросы проверяют глубину знаний и способность адаптироваться. Вместо того чтобы сразу угадывать, скажите: «Позвольте мне немного подумать». Обдуманная пауза лучше уверенно высказанного неправильного ответа.

# Follow-up answers for classic problems
follow_ups = [
    {
        'problem': 'Find duplicate in array 1..n (space O(n) solution uses set)',
        'follow_up': 'Can you do it in O(1) space without modifying input?',
        'answer': 'Floyd cycle detection: treat array as linked list (slow/fast pointer)',
    },
    {
        'problem': 'Reverse a string (space O(n) with new array)',
        'follow_up': 'Can you do it in-place?',
        'answer': 'Two pointers from both ends, swap until they meet: O(n) time O(1) space',
    },
    {
        'problem': 'Find max in array: O(n) single pass',
        'follow_up': 'What if the array is streamed one element at a time?',
        'answer': 'Same algorithm works! Running maximum handles infinite streams',
    },
    {
        'problem': 'Merge sorted arrays O(n+m)',
        'follow_up': 'What if you have K sorted arrays?',
        'answer': 'Use a min-heap of (value, array_idx, element_idx): O(n log k)',
    },
]
for fu in follow_ups:
    print(f'Problem: {fu["problem"]}')
    print(f'Follow-up: {fu["follow_up"]}')
    print(f'Answer: {fu["answer"]}\n')

Практическая задача: группировка анаграмм

Задача: Дан массив строк. Сгруппируйте анаграммы. Верните список групп.

Шаблон: Карта частот в качестве ключа. Для каждой строки отсортируйте её символы с помощью sort (или вычислите кортеж частот символов), чтобы получить канонический ключ. Объединяйте строки по этому ключу, используя хеш-таблицу списков. Временная сложность O(n × m log m), где m — максимальная длина строки, пространственная сложность O(n × m). Вложенные циклы не нужны: достаточно одного прохода по массиву.

from collections import defaultdict

def group_anagrams(strs):
    # Method 1: sort each string as key
    groups = defaultdict(list)
    for s in strs:
        key = ''.join(sorted(s))   # canonical form
        groups[key].append(s)
    return list(groups.values())

def group_anagrams_v2(strs):
    # Method 2: character count tuple as key (avoids sorting)
    groups = defaultdict(list)
    for s in strs:
        count = [0] * 26
        for c in s:
            count[ord(c) - ord('a')] += 1
        key = tuple(count)   # immutable, hashable
        groups[key].append(s)
    return list(groups.values())

test = ['eat', 'tea', 'tan', 'ate', 'nat', 'bat']
result = [sorted(g) for g in group_anagrams(test)]
result.sort()
print('Groups:', result)
# [['ate','eat','tea'], ['bat'], ['nat','tan']]

print('V2:', [sorted(g) for g in sorted(group_anagrams_v2(test), key=len)])

Самооценка после пробного собеседования

После каждого пробного собеседования оцените себя по следующим критериям:

  • Скорость распознавания шаблона: Вы определили шаблон менее чем за <60 секунд?
  • Корректность кода: Ваше первое решение прошло все тестовые случаи?
  • Обработка граничных случаев: Вы протестировали пустые, одиночные и экстремальные входные данные?
  • Коммуникация: Вы объясняли ход рассуждений на протяжении всего решения?
  • Понимание сложности: Вы указали временную и пространственную сложность?
  • Восстановление: Если вы застряли, вы смогли спокойно изменить подход или растерялись?

Оцените себя по каждому критерию от 1 до 5. Посвятите следующую неделю практики критерию с самой низкой оценкой. Большинству кандидатов нужно улучшить либо распознавание шаблонов, либо коммуникацию — редко оба навыка одновременно.

# Self-assessment scoring template
def self_assess(pattern_speed, code_correctness, edge_cases,
                communication, complexity, recovery):
    scores = {
        'Pattern recognition (< 60s)': pattern_speed,
        'Code correctness (all tests pass)': code_correctness,
        'Edge case handling': edge_cases,
        'Communication (thinking aloud)': communication,
        'Complexity stated correctly': complexity,
        'Recovery when stuck': recovery,
    }
    total = sum(scores.values())
    max_total = len(scores) * 5
    print('Self-Assessment Results:')
    print('-'*50)
    for dim, score in scores.items():
        bar = '#' * score + '-' * (5 - score)
        print(f'{dim:45s} [{bar}] {score}/5')
    print(f'\nTotal: {total}/{max_total} ({total/max_total*100:.0f}%)')
    weak = min(scores, key=scores.get)
    print(f'Focus area: {weak}')

self_assess(4, 3, 4, 3, 5, 2)  # example scores

Быстрая проверка

Проверьте, насколько вы поняли концепции «Структуры данных и алгоритмы — подготовка к собеседованию по программированию» из этого урока.

Повторение урока

В этом уроке вы узнали: как решать задачи по фиксированному алгоритму — прочитать условие, определить шаблон за 60 секунд, указать сложность, написать код, а затем протестировать его по пяти категориям граничных случаев, что рабочее решение методом полного перебора лучше незавершённого оптимального решения, когда время заканчивается, а также что самооценка после каждого пробного собеседования по шести критериям (скорость, корректность, граничные случаи, коммуникация, сложность, восстановление) помогает сосредоточить улучшения на нужных областях. Далее мы подробно рассмотрим обработку граничных случаев и лучшие практики коммуникации кандидата на собеседовании.

Часто задаваемые вопросы

Урок «Пробное собеседование с ограничением времени: простые и средние задачи» бесплатный?

Да — полный текст урока «Пробное собеседование с ограничением времени: простые и средние задачи» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс DSA Interview Prep, подпишись на CoddyKit PRO. Курс DSA Interview Prep содержит 4 уроков всего.

Чему я научусь в уроке «Пробное собеседование с ограничением времени: простые и средние задачи»?

Решите три задачи за 45 минут, проговаривая ход мыслей так, как на настоящем собеседовании, а затем разберите оптимальные решения Ты практикуешь DSA Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать DSA Interview Prep?

Предыдущий опыт не требуется. DSA Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Пробное собеседование с ограничением времени: простые и средние задачи»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке DSA Interview Prep?

Да. Каждый урок DSA Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Шпаргалка по распознаванию шаблонов
  2. Пробное собеседование с ограничением времени: простые и средние задачи
  3. Обработка крайних случаев и общение на собеседовании
  4. Разбор сложных задач: «Лестница слов II» и «Инопланетный словарь»
← Назад к DSA Interview Prep