Интервью-вопрос
Есть ли требования к массиву, передаваемому на вход в функцию бинарного поиска
Да, требования есть. Массив должен быть отсортирован по правилу сравнения, а доступ к элементам должен быть быстрым по индексу. Иначе алгоритм может вернуть неправильный результат, хотя код выглядит корректным.
- Добавлен
- Редакция
Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.
Мини-квиз
Проверка перед разбором
Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.
Вопрос 1 из 60 правильно
Разбор
Разобраться, а не зазубрить
Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.
Базовая идея
Бинарный поиск работает не просто потому, что данные лежат в массиве. Он работает потому, что в данных есть порядок. На каждом шаге алгоритм смотрит на середину и решает, какую половину больше не проверять. Такое решение корректно только если все элементы слева не больше среднего, а все элементы справа не меньше среднего, с учетом выбранного направления сортировки.
На интервью можно ответить прямо. Массив должен быть отсортирован, а правило сортировки должно совпадать с правилом сравнения внутри поиска. Для обычных чисел это сортировка по числовому возрастанию или убыванию. Для объектов это сортировка по конкретному полю, например price, id или createdAt.
Что именно требуется от данных
Первое требование, это монотонный порядок. Если алгоритм видит, что target больше среднего элемента, он переходит вправо только потому, что справа находятся значения не меньше среднего. Если это не так, поиск превращается в угадывание.
Второе требование, это доступ по индексу за O(1). В JavaScript-массиве это естественно. В связном списке или потоке данных такой доступ дорогой или невозможный, поэтому бинарный поиск теряет смысл.
Третье требование, это корректное сравнение. Не смешивайте числа, строки, даты и объекты без явного правила. Даже если код запускается, порядок может быть не тем, который вы имеете в виду.
Как выбрать подход на практике
Используйте бинарный поиск и тот же comparator.Чаще выберите линейный поиск, чтобы не платить за сортировку.Отсортируйте один раз или храните индекс, затем ищите быстрее.Держите отдельный порядок или отдельную структуру для каждого ключа.Пример безопасной реализации
В JavaScript важно не забыть comparator при сортировке чисел. Плохая подготовка данных может сломать бинарный поиск еще до запуска самого алгоритма.
Плохой пример:
const values = [1, 10, 2, 20];
values.sort();
// [1, 10, 2, 20], потому что сравнение идет как у строкЗдесь сломается не скорость, а корректность. Числовой поиск будет думать, что массив упорядочен как числа, хотя порядок лексикографический. В UI это может выглядеть как пропавший товар, неверная позиция в списке или пустой результат фильтра.
Безопаснее так:
const values = [1, 10, 2, 20].sort((a, b) => a - b);
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return mid;
}
if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
binarySearch(values, 10);Практический вывод простой. Если вы говорите на интервью, что массив должен быть отсортирован, добавьте по тому же comparator. Так вы покажете, что понимаете не только идею алгоритма, но и реальные ошибки в JavaScript.
React и подготовка данных
Отдельный frontend-риск связан с тем, что Array.prototype.sort() мутирует массив. Если отсортировать массив из props или state прямо перед бинарным поиском, можно изменить порядок данных для других частей компонента. В результате список внезапно перерисуется иначе, выбранный индекс перестанет соответствовать прежнему элементу, а аналитика клика по позиции станет неверной.
Небезопасно:
function ProductList({ products, targetPrice }) {
products.sort((a, b) => a.price - b.price);
const index = binarySearchByPrice(products, targetPrice);
return <ProductCard product={products[index]} />;
}Безопаснее готовить отдельное представление и не делать лишнюю сортировку на каждый рендер:
function ProductList({ products, targetPrice }) {
const sortedByPrice = useMemo(
() => [...products].sort((a, b) => a.price - b.price),
[products]
);
const index = binarySearchByPrice(sortedByPrice, targetPrice);
return index === -1 ? null : <ProductCard product={sortedByPrice[index]} />;
}Практический вывод. Требование к массиву включает не только порядок, но и способ подготовки этого порядка. Во frontend лучше не менять входные данные, явно указать ключ сортировки и обработать случай, когда элемент не найден.
Объекты, дубликаты и frontend-сценарии
Во frontend бинарный поиск редко пишут каждый день вручную, но идея встречается в списках, виртуализации, поиске позиции по времени, ценам, id или координатам. Там данные часто представлены объектами, поэтому вопрос про порядок особенно важен.
Если список товаров отсортирован по цене, можно искать цену бинарным поиском. Если тот же список отсортирован по названию, искать по цене уже нельзя. Для нескольких ключей нужны разные отсортированные представления, индекс, Map для точного поиска по id или обычный линейный проход, если данных мало.
С дубликатами бинарный поиск не ломается. Но обычная версия возвращает одно совпадение, а не обязательно первое. Если вы строите фильтр диапазона, например все товары от 1000 рублей, нужно искать левую границу, а не просто любой элемент со значением 1000.
Когда сортировка не окупается
Не стоит говорить, что перед бинарным поиском всегда надо просто отсортировать массив. Если у вас один поиск по небольшому неотсортированному массиву, линейный поиск часто проще и дешевле. Сортировка стоит O(n log n), а один проход стоит O(n).
Сортировка становится хорошей идеей, когда по тем же данным будет много поисков, когда отсортированный порядок уже нужен для UI или когда данные можно подготовить один раз и переиспользовать. В React это также значит, что сортировку не стоит запускать на каждый рендер без причины. Ее лучше вынести в подготовку данных или мемоизировать по входному массиву. Если поиск запускается после ввода пользователя, учитывайте loading, empty state и вариант не найдено, чтобы UI не показывал старый результат как новый.
Практический вывод
На интервью отвечайте не только фразой, что массив должен быть отсортирован. Лучше сказать полную мысль:
Бинарный поиск требует отсортированного набора данных и быстрого доступа к середине по индексу. Порядок должен совпадать с функцией сравнения. Если массив не отсортирован или отсортирован по другому ключу, алгоритм может отбросить нужную половину и вернуть неверный результат.
Затем можно добавить нюанс по задаче. Для чисел нужен числовой comparator, для объектов нужен ключ сортировки, для дубликатов нужна отдельная логика поиска границы. Такой ответ звучит сильнее, чем простое перечисление условий.
Частые ошибки
Где обычно ошибаются
Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.
- 1
Искать по неотсортированному массиву
Бинарный поиск не проверяет порядок сам по себе. Он просто отбрасывает половину данных по сравнению со средним элементом. На неотсортированном массиве так можно потерять нужное значение. На интервью лучше сказать, что сначала вы гарантируете сортировку или выбираете линейный поиск. - 2
Сортировать по одному полю, а искать по другому
Для массива объектов порядок должен совпадать с ключом поиска. Если список отсортирован поname, бинарный поиск поageнарушит правило, что слева меньше, а справа больше. Сильнее звучит ответ, где вы явно называете общий comparator и ключ сортировки. - 3
Смешивать строки и числа без правила сравнения
В JavaScript сравнения и сортировка могут дать неожиданный порядок, если данные разного типа илиsort()вызван без comparator для чисел. Тогда поиск может пойти не в ту сторону. Лучше нормализовать данные и передать явную функцию сравнения. - 4
Обещать первую позицию при дубликатах
Классический бинарный поиск возвращает найденное совпадение, но не обязан возвращать первый элемент из группы одинаковых значений. Если UI зависит от границы диапазона, например фильтр по цене, вам нужен отдельный поиск левой или правой границы. - 5
Забывать цену предварительной сортировки
Сортировка перед каждым поиском может быть дороже, чем простой проход по массиву. Во frontend это заметно на больших списках, когда операция запускается на каждый ввод в поле поиска. Лучше кэшировать отсортированные данные или выбрать более простую стратегию. - 6
Мутировать данные из props или state перед поиском
sort()меняет исходный массив. В React это может незаметно переставить элементы в состоянии, сломать порядок списка, дать неверную аналитику по позициям или не вызвать ожидаемое обновление UI. Безопаснее делать копию, например[...items].sort(compare), и мемоизировать ее, если список большой.
Follow-up
Что могут спросить дальше
Короткие ответы на вопросы, которыми проверяют понимание условий бинарного поиска и его ограничений.
Живые ответы
Видео с похожим вопросом
Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.
Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.
Какая алгоритмическая сложность у бинарного поиска 😎
Бинарный поиск работает за O(log n), если данные отсортированы и есть быстрый доступ по индексу. Разбираем, как это объяснить на интервью, где теряется выгода и какие ошибки часто делают в JavaScript.
Из-за чего ломается рекурсия при работе с большими числами 😎
Рекурсия в JavaScript чаще всего ломается из-за переполнения стека вызовов, а не из-за самого числа. Разбираем, как объяснить это на интервью и чем заменить глубокую рекурсию во frontend-коде.