Поиск
Бинарный поиск
Когда ячейки таблицы последовательно распределены в памяти с произвольным доступом и имена хранятся в таблице в их естественном порядке, возможен бинарный поиск - один из наиболее широко используемых методов поиска. Идея этого метода состоит в том, чтобы искать имя в интервале, крайними точками которого являются два заданных указателя (для "низа") и (для "верха"). Новый указатель (для "средней точки") устанавливается где-то около середины интервала, и либо с именем в этой ячейке сводит интервал поиска к одному из интервалов или . Если интервал становится пустым, поиск завершается безуспешно.
Для получения логарифмического времени поиска существенно устанавливать указатель за время, не зависящее от длины интервала; это требование делает непригодным бинарный поиск на большинстве вспомогательных запоминающих устройств. Требование, чтобы помещалось точно в середине интервала, несущественно, хотя выбор средней точки в качестве обычно дает самый эффективный алгоритм. В некоторых частных случаях полезно разбить интервал на подинтервалы длины и для фиксированного значения , отличного от . Когда таблица размещена не последовательно, а хранится в виде списка древовидной структуры, доля должна, вероятно, меняться от интервала к интервалу.
Бинарный поиск по идее прост, но с деталями условия завершения поиска нужно обращаться осторожно. Частные случаи и требуют пристального внимания в любой программе бинарного поиска. В алгоритме 13.4 эти случаи обрабатываются тем же кодом, что и в общем случае, и поучительно посмотреть, как это делается, проследив за выполнением алгоритма для и .
Корректность алгоритма 13.4 следует из утверждения, данного в комментарии в начале тела цикла. Он устанавливает, что если находится где-либо в таблице, то оно должно находиться в интервале ; иначе говоря, при нашем предположении, что имя появляется в таблице не больше одного раза, утверждается, что не встречается ни в интервале , ни в интервале . Это утверждение очевидно первый раз, когда мы входим в цикл при и , и непосредственно по индукции проверяется, что оно выполняется при каждом проходе через цикл. Когда мы выходим из цикла, то должно быть , и поэтому утверждение принимает вид и , откуда следует, что .
Оптимальные деревья бинарного поиска
Бинарный поиск в последовательно распределенной таблице ( алгоритм 13.4.) обеспечивает очень быстрое нахождение имен, которые являются средними точками на раннем этапе процесса деления пополам, именно имен, близких к вершине дерева . Таким образом, любое имя в таблице можно выбрать примерно за сравнений.
На практике в большинстве таблиц встречаются имена, к которым обращаются гораздо чаще, чем к другим, и "привилегированные" места в таблице разумно постараться использовать для наиболее часто вызываемых имен, а не для имен, выбранных для этих мест в результате бинарного поиска. Это невозможно осуществить для последовательно распределенных таблиц, поскольку место имени определяется его положением относительно естественного порядка имен в таблице. Введем структуру данных, легко приспосабливаемую как к месту бинарного поиска, так и к возможности выделять точки, в которых таблица делится на две части.
Деревом бинарного поиска над именами называется расширенное бинарное дерево, все внутренние узлы которого помечены различными именами из списка таким образом, что симметричный порядок узлов совпадает с естественным порядком. Каждый из внешних узлов соответствует промежутку в таблице. На рис. 13.1 показаны четыре различных дерева бинарного поиска на множестве имен . Деревья (а) и (b) - вырожденные, поскольку они по существу являются линейными списками, которые должны просматриваться последовательно.
Поиск имени в дереве бинарного поиска осуществляется путем сравнения с именем, стоящим в корне. Тогда
- Если корня нет (дерево пусто), то в таблице отсутствует и поиск завершается безуспешно.
- Если совпадает с именем в корне, поиск завершается успешно.
- Если предшествует имени в корне, поиск продолжается ниже в левом поддереве корня.
- Если следует за именем в корне, поиск продолжается ниже в правом поддереве корня.
Пусть бинарное дерево имеет вид, в котором каждый узел представляет собой тройку (LEFT, NAME, RIGHT), где LEFT и RIGHTсодержат указатели левого и правого сыновей соответственно, и NAME содержит имя, хранящееся в узле. Указатели могут иметь значение , означающее, что поддерево, на которое они указывают пусто. Если указатель корня дерева есть , то само дерево пусто. Как и следовало ожидать, успешный поиск завершается во внутреннем узле дерева бинарного поиска и безуспешный поиск завершается во внешнем узле.
Эта процедура нахождения имени в таблице, организованная в виде дерева бинарного поиска , показана в алгоритме 13.5. Отметим его сходство с алгоритмом 13.4 (бинарный поиск).