Gernar
Frontend DeveloperАлгоритмы и структуры данных

Интервью-вопрос

Что такое бинарный поиск

Бинарный поиск ищет значение в отсортированном диапазоне, каждый шаг отбрасывает половину вариантов и дает сложность O(log n). На интервью важно назвать условие сортировки, аккуратную работу с границами и ограничения в реальном фронтенд-коде.

Добавлен
Редакция

Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.

🐰0
🥚0

Мини-квиз

Проверка перед разбором

Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.

Вопрос 1 из 50 правильно

Что ответить про главное условие бинарного поиска?

Вы объясняете алгоритм коротко и хотите сразу назвать ограничение.

Варианты ответа

Разбор

Разобраться, а не зазубрить

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

Базовая идея

Бинарный поиск полезен, когда у вас уже есть упорядоченные данные. Вместо проверки элементов подряд вы смотрите в середину текущего диапазона. Если средний элемент меньше цели, левая половина уже не нужна. Если больше, не нужна правая половина.

После каждого сравнения остается примерно половина прежнего диапазона. Для массива из миллиона элементов это не миллион проверок, а около двадцати сравнений. На интервью важно не просто сказать O(log n), а спокойно объяснить, откуда берется эта сложность.

Как выглядит надежная реализация

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

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

  while (left <= right) {
    const mid = left + Math.floor((right - left) / 2);

    if (numbers[mid] === target) {
      return mid;
    }

    if (numbers[mid] < target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return -1;
}

Здесь границы включительные. После проверки mid вы исключаете его из следующего диапазона, поэтому используете mid + 1 и mid - 1. Если написать left = mid, на маленьком диапазоне цикл может не сужаться.

Надежная реализация
  1. 1Проверить, что данные отсортированы по тому же правилу.
  2. 2Держать границы left и right включительно.
  3. 3Считать середину через разницу границ.
  4. 4После сравнения сдвигать границу на mid + 1 или mid - 1.
  5. 5Вернуть -1 или явный null, если значение не найдено.
Опасная реализация
  1. 1Искать в массиве, который отсортирован по другому полю.
  2. 2Обновлять границу на mid без сдвига.
  3. 3Не обрабатывать пустой массив и отсутствие значения.
  4. 4Сразу возвращать найденный дубликат, хотя нужен первый.
  5. 5Сортировать исходный массив прямо перед поиском, менять state или props и ломать порядок UI.

Когда выбирать бинарный поиск

Хороший ответ показывает компромисс. Бинарный поиск не лучше всегда. Он хорош, когда порядок данных уже есть или его выгодно поддерживать. Если массив маленький, поиск один, а данные пришли в случайном порядке, линейный поиск часто будет проще и безопаснее.

На фронтенде это особенно заметно. Например, список товаров может отображаться в порядке, выбранном пользователем. Если вы ради поиска вызовете items.sort(), вы измените исходный массив. Это может внезапно поменять порядок в UI, сбить выбранный элемент и дать лишние рендеры. Безопаснее искать линейно в текущем порядке или подготовить отдельную копию: const sortedItems = [...items].sort(comparator).

Как выбрать подход на практике

1Данные уже отсортированы по нужному полю?
Можно рассматривать бинарный поиск.
2Нужно искать один раз в маленьком массиве?
Чаще проще линейный поиск, он понятнее и без риска неверного порядка.
3Поисков много, а данные меняются редко?
Имеет смысл один раз отсортировать копию или хранить данные отсортированными.
4Нужна первая или последняя позиция среди дубликатов?
Пишите отдельный вариант алгоритма, не обычный поиск любого совпадения.

Дубликаты и границы поиска

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

Для первого вхождения нужно при совпадении сохранить текущий индекс и продолжить искать левее:

function findFirst(numbers, target) {
  let left = 0;
  let right = numbers.length - 1;
  let result = -1;

  while (left <= right) {
    const mid = left + Math.floor((right - left) / 2);

    if (numbers[mid] >= target) {
      if (numbers[mid] === target) {
        result = mid;
      }
      right = mid - 1;
    } else {
      left = mid + 1;
    }
  }

  return result;
}

Это полезный нюанс для интервью. Так вы показываете, что понимаете не только шаблон, но и контракт функции.

Практический вывод для Frontend Developer

В реальном фронтенде бинарный поиск встречается не каждый день, но идея полезна. Ее можно применять к отсортированным датам, диапазонам цен, таймлайнам, виртуализированным спискам, брейкпоинтам и поиску порога, при котором условие меняется с false на true.

Хорошая формулировка на интервью может звучать так:

Бинарный поиск нужен, когда данные упорядочены. Я сравниваю цель с серединой диапазона и каждый раз отбрасываю половину, поэтому получаю O(log n). Но я обязательно проверяю, что порядок данных совпадает с comparator, а в коде аккуратно обновляю границы и отдельно решаю, что делать с отсутствием значения и дубликатами.

Такая фраза короткая, но закрывает принцип, сложность, условие применимости и риски реализации.

Частые ошибки

Где обычно ошибаются

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

  1. 1

    Искать в неотсортированном массиве

    Бинарный поиск опирается на порядок. Если массив не отсортирован или отсортирован по другому полю, сравнение с серединой отправит поиск не в ту половину. Безопаснее сначала гарантировать порядок данных или выбрать find для небольшого списка.
  2. 2

    Забывать цену сортировки

    Сам поиск работает за O(log n), но сортировка перед ним обычно стоит O(n log n). Если вы сортируете массив ради одного поиска, итог может быть хуже и сложнее, чем линейный проход.
  3. 3

    Неверно двигать границы

    Если после сравнения поставить left = mid или right = mid, диапазон может перестать сужаться. В итоге цикл зависнет на двух соседних индексах. Для включительных границ используйте mid + 1 и mid - 1.
  4. 4

    Не договориться про дубликаты

    Обычный бинарный поиск возвращает не обязательно первое совпадение. Это ломает задачи вроде вставки в отсортированный список, поиска диапазона дат или подсветки первой позиции. Если важна граница, ищите левую или правую границу отдельно.
  5. 5

    Мутировать массив ради поиска

    В JavaScript метод sort меняет исходный массив. Если отсортировать данные из props или state прямо в компоненте, можно неожиданно изменить порядок элементов в UI, сломать выбранный пользователем порядок и получить лишние перерисовки. Безопаснее создать копию через [...items].sort(comparator) или подготовить отсортированные данные отдельно.

Follow-up

Что могут спросить дальше

Короткие ответы на вопросы, которыми проверяют понимание условий, сложности и реализации бинарного поиска.

Живые ответы

Видео с похожим вопросом

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

Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.

Содержание