Как узнать
отсортированный массив, O(log n) в ограничениях или «наименьшее подходящее значение», когда все большие тоже подходят.
Как решать такие задачи
- Поставь lo и hi на края диапазона, где может быть ответ.
- Посмотри на середину и задай про неё вопрос «да или нет».
- Оставь половину, где ответ ещё может быть. Сдвигай lo или hi за mid, чтобы диапазон всегда сужался.
- Остановись, когда нашёл. Если lo обогнал hi, ответа нет.
Вариант: Поиск по ответу
То же, но ищем не элемент в массиве, а сам ответ, например наименьшую скорость. Работает, когда любой вариант легко проверить, а проверки идут «нет, нет, …, да, да»: граница между «нет» и «да» и есть ответ.
- Как узнать: просят «наименьшее x, такое что ...» (или наибольшее), а проверить одно данное x легко.
- Найди границы: lo — наименьшее x, которое может подойти, hi — значение, которое точно подходит, здесь это самая большая кучка.
- Напиши feasible(x) — проверку «да или нет» — и убедись, что она монотонна: если для x ответ «да», то и для любого большего x тоже «да».
- Повторяй, пока lo < hi: если feasible(mid), ставь hi = mid, чтобы mid остался в диапазоне, иначе lo = mid + 1. Когда они встретятся, lo — самое левое подходящее значение.
Вариант: Повёрнутый массив
То же, но массив повёрнут, будто его разрезали и переставили куски. Если разрезать его посередине, одна половина всё равно отсортирована, и по ней видно, куда идти дальше.
- Как узнать: массив «отсортирован, но повёрнут» или сначала растёт, а потом падает, а нужно всё равно O(log n).
- На каждом mid найди половину, которой можно верить: сравни nums[lo] и nums[mid] и пойми, какая половина отсортирована.
- Проверь, лежит ли цель между краями отсортированной половины. Если да, оставь её, если нет — другую.
- Минимум повёрнутого массива ищут так же, сравнивая nums[mid] с nums[hi]: если nums[mid] > nums[hi], спуск и минимум правее mid, иначе на mid или левее.
- Пик ищут, сравнивая nums[mid] с соседом nums[mid + 1]: если сосед больше, пик правее, иначе на mid или левее.
Где встречается
- git bisect. Ищет коммит, который что-то сломал. Берёт коммит посередине между «ещё работало» и «уже сломано», ты его проверяешь, и половина истории отбрасывается. Даже среди 1000 коммитов виновника находят примерно за 10 проверок.
- Индексы в базах данных. Внутри каждой страницы индекса ключи лежат по порядку, и PostgreSQL находит нужный делением пополам, а не перебором.
- Python. Модуль bisect находит, куда вставить число в отсортированный список, чтобы порядок не сломался.