Как решать задачи на собеседовании по JavaScript

Две трети задач на массивы на собеседованиях решаются двумя паттернами: sliding window и two pointers. Если вы их
освоите — большинство задач будет сводиться к распознаванию «а, это sliding window» и написанию 15 строк кода.

Разбираем оба паттерна: когда использовать, как реализовать и на каких задачах тренироваться.


Sliding window

Суть

Массив или строка — окно фиксированной или переменной длины «едет» по ним, и на каждом шаге мы обновляем результат.
Вместо вложенных циклов — один проход, O(n).

Когда использовать

  • Максимальная/минимальная сумма подмассива фиксированной длины
  • Максимальная длина подмассива с условием
  • Подсчёт подстрок с уникальными символами

Шаблон

function slidingWindow(arr, k) {
    let windowSum = 0;
    let maxSum = -Infinity;

    for (let i = 0; i < arr.length; i++) {
        windowSum += arr[i];

        if (i >= k) {
            windowSum -= arr[i - k];
        }

        if (i >= k - 1) {
            maxSum = Math.max(maxSum, windowSum);
        }
    }

    return maxSum;
}

Задача 1: Максимальная сумма подмассива фиксированной длины

Дан массив nums и число k. Найдите максимальную сумму подмассива длины k.

function maxSumSubarray(nums, k) {
    let windowSum = 0;

    for (let i = 0; i < k; i++) {
        windowSum += nums[i];
    }

    let maxSum = windowSum;

    for (let i = k; i < nums.length; i++) {
        windowSum += nums[i] - nums[i - k];
        maxSum = Math.max(maxSum, windowSum);
    }

    return maxSum;
}

maxSumSubarray([2, 1, 5, 1, 3, 2], 3); // 9 (подмассив [5, 1, 3])

Сложность: O(n) время, O(1) память.

Задача 2: Длина самого длинного подмассива с суммой ≤ target

Дан массив положительных чисел nums и число target. Найдите максимальную длину подмассива, сумма которого не
превышает target.

function longestSubarray(nums, target) {
    let windowSum = 0;
    let left = 0;
    let maxLen = 0;

    for (let right = 0; right < nums.length; right++) {
        windowSum += nums[right];

        while (windowSum > target) {
            windowSum -= nums[left];
            left++;
        }

        maxLen = Math.max(maxLen, right - left + 1);
    }

    return maxLen;
}

longestSubarray([3, 1, 2, 7, 4, 2, 1, 1, 5], 8); // 4

Внутренний while сдвигает левую границу, пока условие не выполнится. Это variable-size sliding window — длина окна
меняется.

Задача 3: Максимальное количество единиц после замены нулей

Дан массив из 0 и 1 и число k — максимальное количество нулей, которое можно заменить на единицы. Найдите длину самого
длинного подмассива единиц.

function longestOnes(nums, k) {
    let left = 0;
    let zeros = 0;
    let maxLen = 0;

    for (let right = 0; right < nums.length; right++) {
        if (nums[right] === 0) zeros++;

        while (zeros > k) {
            if (nums[left] === 0) zeros--;
            left++;
        }

        maxLen = Math.max(maxLen, right - left + 1);
    }

    return maxLen;
}

longestOnes([1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], 2); // 6

Ключевая идея: окно «допускает» до k нулей. Когда нулей больше — сдвигаем левую границу.


Two pointers

Суть

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

Когда использовать

  • Задачи на пары в отсортированном массиве
  • Палиндромы и сравнение с концов
  • Слияние отсортированных массивов

Шаблон

function twoPointers(arr) {
    let left = 0;
    let right = arr.length - 1;

    while (left < right) {
        const sum = arr[left] + arr[right];

        if (sum === target) return [left, right];
        if (sum < target) left++;
        else right--;
    }

    return [-1, -1];
}

Задача 4: Два числа с заданной суммой

Дан отсортированный массив nums и число target. Найдите индексы двух элементов, сумма которых равна target.

function twoSumSorted(nums, target) {
    let left = 0;
    let right = nums.length - 1;

    while (left < right) {
        const sum = nums[left] + nums[right];

        if (sum === target) return [left, right];
        if (sum < target) left++;
        else right--;
    }

    return [-1, -1];
}

twoSumSorted([2, 7, 11, 15], 9); // [0, 1]
twoSumSorted([1, 2, 3, 4, 6], 6); // [1, 3]

Сложность: O(n) время, O(1) память. Вместо хэш-таблицы — просто два указателя.

Задача 5: Палиндром (проверка)

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

function isPalindrome(s) {
    let left = 0;
    let right = s.length - 1;

    while (left < right) {
        while (left < right && !/[a-z0-9]/i.test(s[left])) left++;
        while (left < right && !/[a-z0-9]/i.test(s[right])) right--;

        if (s[left].toLowerCase() !== s[right].toLowerCase()) return false;

        left++;
        right--;
    }

    return true;
}

isPalindrome("A man, a plan, a canal: Panama"); // true
isPalindrome("race a car"); // false

Указатели прыгают через не-алфавитные символы и сравнивают по краям к центру.

Задача 6: Удаление дубликатов из отсортированного массива

Удалите дубликаты на месте. Верните количество уникальных элементов.

function removeDuplicates(nums) {
    if (nums.length === 0) return 0;

    let slow = 0;

    for (let fast = 1; fast < nums.length; fast++) {
        if (nums[fast] !== nums[slow]) {
            slow++;
            nums[slow] = nums[fast];
        }
    }

    return slow + 1;
}

removeDuplicates([1, 1, 2, 3, 3]); // 3, массив → [1, 2, 3, ...]

Здесь два указателя движутся в одном направлении: fast ищет новые элементы, slow записывает их на позицию|unique.


Комбинированные задачи

Задача 7: Подстрока без повторяющихся символов

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

function lengthOfLongestSubstring(s) {
    const seen = new Set();
    let left = 0;
    let maxLen = 0;

    for (let right = 0; right < s.length; right++) {
        while (seen.has(s[right])) {
            seen.delete(s[left]);
            left++;
        }

        seen.add(s[right]);
        maxLen = Math.max(maxLen, right - left + 1);
    }

    return maxLen;
}

lengthOfLongestSubstring("abcabcbb"); // 3
lengthOfLongestSubstring("bbbbb"); // 1

Это sliding window + hash set. Окно расширяется вправо, сжимается слева при повторе.

Задача 8: Слияние двух отсортированных массивов

function merge(nums1, m, nums2, n) {
    let i = m - 1;
    let j = n - 1;
    let k = m + n - 1;

    while (i >= 0 && j >= 0) {
        if (nums1[i] > nums2[j]) {
            nums1[k--] = nums1[i--];
        } else {
            nums1[k--] = nums2[j--];
        }
    }

    while (j >= 0) {
        nums1[k--] = nums2[j--];
    }

    return nums1;
}

merge([1, 2, 3, 0, 0, 0], 3, [2, 5, 6], 3); // [1, 2, 2, 3, 5, 6]

Два указателя идут с конца обоих массивов, записывая в конец nums1.


Как выбрать паттерн

Условия задачиПаттерн
Подмассив фиксированной длиныSliding window (fixed)
Подмассив с условием (сумма, уникальность)Sliding window (variable)
Отсортированный массив + параTwo pointers (с концов)
Удаление/запись на местеTwo pointers (slow/fast)
Палиндром, сравнение строк с краёвTwo pointers

Где тренироваться

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

Попробуйте решить задачи на Девстанции — здесь есть десятки задач, которые тренируют sliding window и two pointers. AI
проверит ваш код и подскажет, если есть более эффективное решение.

А если готовитесь к собеседованию —
посмотрите чеклист из 50 вопросов
и план подготовки за неделю.