Бинарный (двоичный) поиск / Binary search на Python
Мы познакомились с линейным поиском, теперь настала очередь бинарного (двоичного).
В чем же он заключается?
Есть отсортированный список чисел, берем число из его середины. Если это число больше искомого, то поиск продолжаем в левой половине списка, если меньше искомого - в правой.
Продолжаем поиск, когда находим искомое число, возвращаем его индекс, иначе делаем вывод, что такого числа нет в списке.
Как утверждается в этой статье, только 10% программистов способны написать двоичный поиск.
Поэтому рекомендую сначала реализовать самим на любимом ЯП, а потом прочитать дальше и поделиться результатом.
Возвращаясь к нашей книжной полке, предположим, что все книги отсортированы по автору, слева направо. То есть вначале полки находится книга Ахматовой, а в конце - Шолохов.
Нам же надо найти книгу, автор которой Пушкин.
Берем книгу посередине полки, смотрим, кто является ее автором. С первого раза мы потерпели неудачу, и автором данной книги оказался Тютчев.
Вспоминая, что книги отсортированы по автору, мы понимаем, что правее данной книги искомой нет, следовательно, надо искать левее.
Поэтому из поиска мы исключаем правую половину полки. Теперь возьмем книгу посередине левой половины полки и посмотрим, кто ее автор. Например, Лесков.
Значит, искомая книга находится правее. Далее берем книгу посреди оставшейся части полки. Если это та книга, которую мы искали, то все шикарно, иначе снова исключаем половину (левую или правую, в зависимости от автора) и тд.
В итоге мы либо находим искомую книгу, либо добираемся до такой малой части полки, где не будет ни одной книги. Тогда можем с увереностью сказать, что искомой книги на полке нет.
Скажу сразу, я нигде не нашел стандартного условия, описывающего ситуацию с дубликатами на полке. Поэтому будем считать, что дублей у нас не будет, но если и будут, то будем возвращать первый найденный индекс.
Процедура будет принимать те же входные параметры, что и в линейном поиске:
Здесь мы на всякий случай сортируем список.
p и r - наши первые границы. q - середина рассматриваемого “подсписка”, среднее значение суммы p и r, без дробной части.
Искомый элемент найден, если значение середины “подсписка” lst[q] равно x.
Если lst[q] > x, мы исключаем все элементы справа от q.
Если lst[q] < x, мы исключаем все элементы слева до q.
Программисты допускают ошибки в следующих ситуациях:
- Есть повторяющиеся элементы.
- Список либо пуст, либо содержит 1 элемент, либо 2.
- Искомое число находится в начале или в конце списка.
- Искомого числа нет в списке.
Поэтому напишем тесты, которые желательно всегда писать в самом начале. Если я что-то забыл или пропустил - пишите, исправлю.
Код по-прежнему можно найти здесь.