Алгоритмические задачи на JavaScript полезно решать по одному циклу: уточнить вход и выход, придумать простой вариант, проверить крайние случаи, оценить сложность и только потом оптимизировать. Начните с массивов, строк, Map и Set, затем добавьте два указателя, стек, очередь, сортировку и бинарный поиск.
Задача не проверяет способность вспомнить красивый трюк. Она проверяет моделирование данных и аргументацию. В JavaScript массив является универсальной индексируемой коллекцией, Map хранит пары ключ-значение, Set поддерживает уникальность. Уточняйте, можно ли менять исходный массив, нужны ли индексы или значения, допустимы ли дубли и как обрабатывать отсутствие ответа.
Пройдите алгоритм руками на пяти элементах.
Разделите хранение состояния и обход.
Пустой ввод, дубли, отрицательные и нет решения.

Алгоритмические задачи на JavaScript: пара с заданной суммой
Нужно вернуть индексы двух разных элементов, сумма которых равна target. Двойной цикл прост, но требует квадратичного числа сравнений. Map позволяет за один проход хранить уже встреченные значения и искать дополнение target - value. До кода проговорите поведение при нескольких ответах: пример возвращает первую найденную пару.
function findPair(numbers, target) {
const seen = new Map();
for (let index = 0; index < numbers.length; index += 1) {
const value = numbers[index];
const needed = target - value;
if (seen.has(needed)) {
return [seen.get(needed), index];
}
// Сохраняем индекс уже просмотренного значения
seen.set(value, index);
}
return null;
}
console.log(findPair([2, 7, 11, 15], 9)); // [0, 1]- Для [2, 7, 11, 15] и 9 функция возвращает [0, 1]
- Один проход использует дополнительную память Map
Время работы в среднем линейно по числу элементов при обычном поведении Map, память тоже линейна в худшем случае. Проверьте [3, 3] с target 6: первый индекс уже лежит в Map, когда обход доходит до второго элемента. Проверьте пустой массив и отсутствие ответа. Если вход отсортирован и нужны значения, можно использовать два указателя без Map, но контракт должен позволять потерю исходных индексов или их отдельное сохранение.
12 задач в порядке роста
| Элемент | Что означает | Что делать |
|---|---|---|
| Массивы | Максимум, разворот, частоты | Цикл и accumulator |
| Строки | Палиндром, скобки, анаграмма | Нормализация и стек |
| Map и Set | Дубли, две суммы, пересечение | Быстрый поиск |
| Указатели | Отсортированные пары, окно | Инвариант границ |
| Поиск | Бинарный поиск и ответ | Сокращение диапазона |
Для каждой задачи храните три версии: постановка, собственное решение и тесты. Не оптимизируйте до рабочего простого варианта. Сортировка может упростить алгоритм, но стоит O(n log n) и иногда меняет исходный массив. Метод sort без comparator сортирует значения как строки, поэтому для чисел используйте (a, b) => a - b. Это частый источник неожиданных результатов.

Практика: соберите two sum на Map и проверьте крайние случаи
Создайте файл findPair.js и перенесите в него функцию из статьи вместе с пятью вызовами console.log. Успехом считайте такой прогон: findPair([2, 7, 11, 15], 9) возвращает [0, 1], а вход без ответа возвращает null. Задача закрыта, когда вы можете вслух назвать число проходов по массиву и объём памяти под Map.
- Контракт two sum. Запишите комментарием в начале файла: на входе массив чисел и target, на выходе два индекса или null. Там же отметьте, допустимы ли дубли и разрешено ли менять исходный массив.
- Наивный двойной цикл. Напишите findPairSlow с двумя вложенными циклами и счётчиком сравнений. На массиве из 1000 случайных чисел счётчик покажет сотни тысяч сравнений, и это ваша точка отсчёта.
- Версия на Map. Перепишите функцию через new Map() и проверку seen.has(target - value), сохраняя индекс после проверки. Выведите оба ответа рядом: значения совпадают, а счётчик падает до длины массива.
- Тест на дубли. Вызовите findPair([3, 3], 6) и убедитесь, что вернулись индексы [0, 1], а не один и тот же элемент. Это и есть доказательство, что seen.set идёт после seen.has.
- Границы и sort. Прогоните пустой массив, массив из одного элемента, отрицательные числа и вход без пары: везде должен вернуться null. Отдельно отсортируйте [10, 9, 100] через sort() и через sort((a, b) => a - b) и сравните два вывода.
Переставьте seen.set(value, index) выше проверки seen.has(needed) и снова запустите findPair([3, 3], 6). Функция вернёт [0, 0], то есть сложит элемент сам с собой: значение попадает в Map раньше, чем ищется дополнение. Второй излом сделайте на сортировке: вызовите [10, 9, 100].sort() без comparator и посмотрите на [10, 100, 9], где 100 стоит перед 9 из-за сравнения строк. Верните обе строки как было и убедитесь, что [0, 1] и числовой порядок вернулись.
Дальше решите ту же пару с заданной суммой двумя указателями на отсортированном массиве: сумма больше target - двигаете правую границу, меньше - левую, дополнительная память не нужна. Сравните контракты: версия с указателями отдаёт значения, а исходные индексы теряются после сортировки. Потом идите по списку из 12 задач в его порядке: частоты через Map, палиндром и скобки через стек, затем бинарный поиск. Для каждой задачи храните три файла: постановку, своё решение и тесты.
Частые ошибки и почему они появляются
Копирование решения после двух минут лишает задачу главной пользы. Вторая ошибка: назвать O(n), не определив n и операции. Третья: проверить только пример из условия. Рабочая подготовка требует написать тесты до просмотра чужого решения и вернуться к задаче через несколько дней без подсказки.
Уточните формат ответа, дубли, mutability и отсутствие решения.
Копируйте массив, если исходный должен сохраниться.
Посчитайте проходы, lookup и дополнительную память.
Самопроверка
Зачем нужен Map в задаче two sum?
Map в two sum хранит уже просмотренные значения вместе с их индексами. За счёт этого дополнение target - value ищется за один проход, без вложенного цикла. Платой становится память: в худшем случае в Map окажется весь массив.
Почему sort для чисел вызывают с (a, b) => a - b?
Метод sort без comparator приводит элементы к строкам и сравнивает их посимвольно, поэтому [10, 9, 100] превращается в [10, 100, 9]. Comparator (a, b) => a - b задаёт числовое сравнение. Помните и о втором эффекте: sort меняет исходный массив, так что копируйте его, если оригинал нужен дальше.
Когда удобнее решать задачу двумя указателями?
Два указателя удобны, когда вход отсортирован и условие позволяет двигать границы навстречу друг другу. Дополнительная память при этом не тратится, в отличие от решения на Map. Минус в том, что после сортировки исходные индексы теряются, если не сохранить их отдельно.
Что должна возвращать функция, если пары с нужной суммой нет?
Функция возвращает null, когда ни для одного элемента дополнение не нашлось в Map. Этот случай проговаривают в контракте до кода, иначе вызывающая сторона попробует разложить null в массив и упадёт. Пустой массив и массив из одного элемента попадают сюда же.
Как проверить алгоритм на дубликатах?
Дубликаты проверяются входом [3, 3] с target 6: корректная функция вернёт [0, 1], то есть два разных индекса. Если пришло [0, 0], значит запись в Map выполняется раньше проверки и элемент складывается сам с собой. Такой тест стоит держать рядом с основным примером [2, 7, 11, 15].
С каких задач начинать подготовку на JavaScript?
Начинайте с массивов и строк: максимум, разворот, частоты, палиндром, скобки, анаграмма. Дальше подключайте Map и Set для дублей и пересечений, потом два указателя для отсортированных данных и бинарный поиск. В подборке 12 задач, и порядок в ней важнее скорости прохождения.
Что делать дальше
Решите 12 задач, повторите четыре из них через неделю и объясните сложность вслух. Основы языка систематизирует курс JavaScript. Выбор первого языка сравните в статье Python или JavaScript, а формат интервью в разборе вопросов junior.
