Этап 8 · урок 1
Точный бинарный поиск через инвариант
Бинарный поиск безопасно отбрасывает половину только благодаря отсортированности и явно выбранным границам.
После урока вы сможете
- поддерживать инвариант закрытого диапазона
- избегать переполнения midpoint и зависания границ
В [1,3,5,7,9] при поиске 7 середина сначала равна 5: левая половина отбрасывается, следующая середина равна 7, ответ — индекс 3.
Как найти точное значение в отсортированном массиве?
Найти индекс target в отсортированном массиве или вернуть -1.
Линейный просмотр до совпадения
Просмотреть элементы слева направо до совпадения за O(n).
Почему порядок должен позволить отбрасывать половину?
Линейный поиск не использует порядок и отбрасывает только один элемент за сравнение.
Середина доказывает бесполезность одной стороны
Сравнение с серединой доказывает, в какой половине target не может находиться.
Где может оставаться target в начале итерации?
В начале каждой итерации, если target присутствует, хотя бы одно его вхождение находится в закрытом диапазоне [lo,hi].
Сужаем закрытый диапазон [lo, hi]
Пока lo <= hi, вычислить mid = lo + (hi-lo)/2. При равенстве вернуть mid; если значение меньше target, сделать lo=mid+1, иначе hi=mid-1.
Точный бинарный поиск на C++17 и Python 3
C++17
#include <cassert>
#include <vector>
using namespace std;
int binarySearch(const vector<int>& values, int target) {
int lo = 0;
int hi = static_cast<int>(values.size()) - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (values[mid] == target) return mid;
if (values[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;
}
int main() {
assert(binarySearch({1, 3, 5, 7, 9}, 7) == 3);
assert(binarySearch({1, 3, 5}, 2) == -1);
assert(binarySearch({}, 2) == -1);
}
Python 3
def binary_search(values: list[int], target: int) -> int:
lo = 0
hi = len(values) - 1
while lo <= hi:
mid = lo + (hi - lo) // 2
if values[mid] == target:
return mid
if values[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
assert binary_search([1, 3, 5, 7, 9], 7) == 3
assert binary_search([1, 3, 5], 2) == -1
assert binary_search([], 2) == -1
Сколько раз можно делить диапазон пополам?
O(log n) времени и O(1) памяти: длина диапазона хотя бы вдвое уменьшается. Вход обязан быть отсортирован по тому же порядку сравнения.
Пустой массив, крайние индексы и безопасный mid
Пустой массив даёт hi=-1; один элемент; target на концах; дубликаты — возвращается любое вхождение; большие индексы.
Тесты на найденный и отсутствующий target
[],5 -> -1, [4],4 -> 0, [1,3,5],1/5, отсутствующий 2, массив с дубликатами.
Когда условие обещает отсортированность?
Данные отсортированы и нужно найти точное значение за логарифмическое число сравнений.
Когда бинарный поиск неприменим к данным?
На неотсортированных данных или при дорогом случайном доступе (обычный linked list) эта реализация не подходит.
Мини-проверка: почему lo становится mid + 1?
Вопрос: почему при a[mid] < target можно убрать и mid? Ответ: mid не равен target, а все позиции левее имеют не большее значение.
Мини-проверка: зачем вычислять mid через разность границ?
Вопрос: зачем mid = lo + (hi-lo)/2? Ответ: это избегает сложения двух потенциально больших индексов.
Проследите границы при поиске 7
Проследите поиск 7 в [1,3,5,7,9,11], записывая lo, hi, mid и исключённую половину.
Найдите значение двоичным поиском самостоятельно
Задача 1
Найдите target в отсортированном по убыванию массиве или верните -1, самостоятельно выбрав и обосновав направления сдвига.
Инвариант оправдывает каждое отбрасывание половины
Смысл бинарного поиска — не формула середины, а доказательство, что отброшенная половина не содержит ответа.
Типичная ошибка
Граница должна исключать уже проверенную середину
Самая частая ошибка бинарного поиска не в формуле mid, а в обновлении диапазона без гарантированного прогресса.
- Ошибка
left = mid, если values[mid] < target- Почему ломается
- При двух соседних индексах mid может совпасть с left. Диапазон не уменьшится, и цикл повторит то же состояние.
- Безопасный ход
- Для закрытого диапазона присвойте left = mid + 1: значение в mid уже проверено и точно не является ответом.
Интерактивная трассировка
Точный поиск значения 11
Инвариант: если значение есть, оно остаётся в закрытом диапазоне [lo, hi].
- 10
- 31
- 32
- 63
- 84
- 115
- 146
- 197
Начальное состояние: значение 11 может находиться в закрытом диапазоне [0, 7].
Не начат
Закрепление
Попробовать самостоятельно
Небольшой маршрут от разбора к самостоятельному решению. Статусы синхронизируются с общей практикой.
Двоичный поиск
Зафиксируйте смысл границ и проверяйте его на пустом массиве, крайних позициях и отсутствии ответа.
Сначала завершите уроки-зависимостиGuess Number Higher or Lower
Перед циклом зафиксируй, входят ли границы в область поиска, и сохраняй этот контракт после ответа проверки.
Сначала завершите уроки-зависимостиFind Peak Element
По сравнению соседей определяй сторону, на которой гарантированно остаётся хотя бы один пик.
Сначала завершите уроки-зависимости