
Бинарный поиск
Еще одним из известных рекурсивных алгоритмов является БИНАРНЫЙ ПОИСК. Если организовать хранение данных таким образом, что пары ключ-значение хранятся в виде массива, а также изначально отсортированы по возрастанию ключей, представляется возможным существенно повысить быстродействие процедуры поиска элемента множества или отображения по сравнению с простейшим линейными поиском. Безусловно, сортировка данных занимает ощутимое время. Однако программист может столкнуться с ситуацией, когда необходимо обрабатывать уже отсортированные кем-то данные, получаемые, например, из внешнего источника - дискового файла или базы данных - в уже упорядоченном виде.
Пусть имеется заранее упорядоченное по алфавиту множество названий государств, входящих в Шенгенскую зону:
const char * const g_SchengenCountries [] = {
“Austria”
, “Belgium”
, “Czech Republic”
, “Denmark”
, “Estonia”
, “Finland”
, “France”
, “Germany”
, “Greece”
, “Hungary”
, “Iceland”
, “Italy”
, “Latvia”
, “Liechtenstein”
, “Lithuania”
, “Luxembourg”
, “Malta”
, “Netherlands”
, “Norway”
, “Poland”
, “Portugal”
, “Slovakia”
, “Slovenia”
, “Spain”
, “Sweden”
, “Switzerland”
};
Требуется написать функцию, определяющую входит ли интересующая страна в Шенгенскую зону. На каждом шаге выбираем ключ в середине интервала и сравниваем его с искомым ключом. Если ключи равны, необходимое значение найдено. Если искомый ключ меньше, далее анализируем первую половину данных. Если больше - вторую.
bool isSchengenCountry ( const char* _country )
{
int minIndex = 0;
int maxIndex = sizeof( g_SchengenCountries ) / sizeof( const char * );
while ( minIndex < maxIndex )
{
int midIndex = ( minIndex + maxIndex ) >> 1;
int cmpResult = strcmp( _country, g_SchengenCountries[ midIndex ] );
if ( ! cmpResult )
return true;
else if ( cmpResult < 0 )
maxIndex = midIndex;
else
minIndex = midIndex + 1;
}
return false;
}
Предположим, происходит вызов данной функции с аргументом “Finland”. Пошаговое выполнение алгоритма бинарного поиска выглядит следующим образом:
На первом шаге интервал поиска составляет 26 стран от индекса 0 до индекса 25 включительно. Первое сравнение происходит со страной с индексом 13, а именно с “Liechtenstein”. По алфавиту искомая страна “Finland” идет раньше, чем “Liechtenstein”, соответственно поиск продолжается в первой половине массива.
На втором шаге интервал поиска составляет 13 стран от индекса 0 до индекса 12 включительно. Следующее сравнение происходит со страной с индексом 6, т.е. “France”. Искомая страна “Finland” также раньше по алфавиту, и поиск переходит в первую четверть.
На третьем шаге интервал поиска составляет 6 стран от индекса 0 до индекса 5 включительно. Значение, которое берется для сравнения - “Denmark” с индексом 3. На этот раз поиск будет продолжен во второй половине текущего интервала.
На четвертом шаге интервал поиска сужается до 2 стран от индекса 4 до индекса 5 включительно. Сравнение происходит со строкой “Finland” по индексу 5, таким образом образуется итоговый утвердительный ответ.
Вычислительная сложность данной функции оценивается как O(log N), поскольку на каждом шаге алгоритма отсекается половина неправильных результатов. Более строго это можно вывести исходя из рекуррентного соотношения:
Соотношение имеет указанную форму,
поскольку на каждом шаге алгоритма
решается аналогичная задача на вдвое
меньшем объеме данных. При этом на каждом
шаге дополнительно выполняется некоторая
элементарная вычислительная работа, в
частности, сравнение искомого ключа и
ключа в середине массива, а затем выбор
следующего интервала в зависимости от
результата сравнения. Раскрыть данное
рекуррентное соотношение с целью
получения оценки вычислительной
сложности можно при помощи подстановки
,
где M - некоторое целое число, степенью
двойки которого является N. Несмотря на
то, что такая подстановка несправедлива
в общем случае, т.к. N не обязан являться
степенью двойки в каждой задаче, такое
предположение можно заложить, представив,
что исходный набор данных дополняется
до ближайшей степени двойки сверху
ненужными значениями, например, нулями.
Осуществим задуманную подстановку и
начнем раскрытие рекурсии поэтапно:
Из логарифмической вычислительной сложности вытекает, что в худшем случае поиск интересующей страны в составе стран Шенгенской зоны потребует не более 5 сравнений строк. Худшим случаем для данного алгоритма является поиск значения, отсутствующего в наборе. 5 сравнений - это значительно лучше, чем 26 сравнений в худшем случае при линейном поиске.