awesome-everything EN
↑ Обратно к восхождению

Алгоритмы с нуля

Массивы и строки: чтение кода

Суть Читай реальные сниппеты на two pointers, sliding window, prefix sum и in-place, затем предскажи вывод, назови сложность или найди баг, который поймает тест.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 14 min

Приём легко проговорить и легко сломать. Читай каждый сниппет как на ревью: проследи указатели, проверь границу и реши, делает ли цикл то, что думает его автор.

Цель

Потренируй чтение приёмов юнита в коде — предсказание вывода, точную сложность и поиск off-by-one или отсутствующего предусловия до того, как это уйдёт в прод.

Сниппет 1 — вариативное окно

function minSubarrayLen(arr, target) {
  let left = 0, sum = 0, best = Infinity;
  for (let right = 0; right < arr.length; right++) {
    sum += arr[right];
    while (sum >= target) {
      best = Math.min(best, right - left + 1);
      sum -= arr[left];
      left++;
    }
  }
  return best === Infinity ? 0 : best;
}
// minSubarrayLen([2, 3, 1, 2, 4, 3], 7)
Викторина

Что вернёт этот вызов и какова временная сложность алгоритма?

Сниппет 2 — уплотнение in-place

function removeVal(arr, val) {
  let write = 0;
  for (let read = 0; read < arr.length; read++) {
    if (arr[read] !== val) {
      arr[write] = arr[read];
      write++;
    }
  }
  return write;
}
// const a = [3, 2, 2, 3]; const k = removeVal(a, 3);
Викторина

После вызова — чему равно k и каково состояние первых k ячеек a?

Сниппет 3 — two-sum с концов

function twoSum(arr, target) {
  let left = 0, right = arr.length - 1;
  while (left < right) {
    const sum = arr[left] + arr[right];
    if (sum === target) return [left, right];
    else if (sum < target) left++;
    else right--;
  }
  return null;
}
// twoSum([8, 2, 5, 1, 7], 9)
Викторина

Массив [8, 2, 5, 1, 7] не отсортирован. Корректная пара с суммой 9 существует (8 + 1 или 2 + 7). Что этот код реально вернёт и почему?

Сниппет 4 — запрос суммы на диапазоне через prefix sum

function buildPrefix(arr) {
  const p = new Array(arr.length + 1).fill(0);
  for (let i = 0; i < arr.length; i++) p[i + 1] = p[i] + arr[i];
  return p;
}
function rangeSum(p, i, j) { return p[j] - p[i - 1]; }
// const p = buildPrefix([2, 5, 1, 3, 4]);  // p = [0, 2, 7, 8, 11, 15]
// rangeSum(p, 1, 3)  // задумано: arr[1] + arr[2] + arr[3] = 9
Викторина

buildPrefix корректна, но в rangeSum есть off-by-one. Что вернёт rangeSum(p, 1, 3) и какой фикс?

Итог

Чтение приёмов в коде — там, где прячутся баги. Вариативное окно — O(n), потому что left движется только вперёд: внутренний while амортизирован, а не вложен. Уплотнение in-place гарантирует только a[0..k-1]; хвост устарел по контракту. Two-sum с концов несостоятелен на неотсортированном входе, даже когда случайно возвращает пару. А запросы prefix sum живут или умирают на конвенции n+1: p[k] — сумма первых k элементов, поэтому формула диапазона — p[j+1] − p[i]. Проследи указатели и проверь границу, прежде чем доверять циклу.

Продолжить восхождение ↑Массивы и строки: движок запросов по логу
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.