Новосибирский Государственный Университет
Опубликован: 08.11.2006 | Доступ: свободный | Студентов: 1940 / 96 | Оценка: 4.27 / 4.09 | Длительность: 12:16:00
Специальности: Программист
Лекция 13:

Поиск

< Лекция 12 || Лекция 13: 1234 || Лекция 14 >

Бинарный поиск

Когда ячейки таблицы последовательно распределены в памяти с произвольным доступом и имена хранятся в таблице в их естественном порядке, возможен бинарный поиск - один из наиболее широко используемых методов поиска. Идея этого метода состоит в том, чтобы искать имя z в интервале, крайними точками которого являются два заданных указателя l (для "низа") и h (для "верха"). Новый указатель m (для "средней точки") устанавливается где-то около середины интервала, и либо z с именем в этой ячейке сводит интервал поиска к одному из интервалов [l,m - 1] или [m +
1,h]. Если интервал становится пустым, поиск завершается безуспешно.

Для получения логарифмического времени поиска существенно устанавливать указатель m за время, не зависящее от длины интервала; это требование делает непригодным бинарный поиск на большинстве вспомогательных запоминающих устройств. Требование, чтобы m помещалось точно в середине интервала, несущественно, хотя выбор средней точки в качестве m обычно дает самый эффективный алгоритм. В некоторых частных случаях полезно разбить интервал на подинтервалы длины \alpha (h - l + 1) и (1 - \alpha )(h - l + 1) для фиксированного значения \alpha, отличного от \frac{1}
{2}. Когда таблица размещена не последовательно, а хранится в виде списка древовидной структуры, доля \alpha должна, вероятно, меняться от интервала к интервалу.

Бинарный поиск по идее прост, но с деталями условия завершения поиска нужно обращаться осторожно. Частные случаи h - l = 1 и h -
l = 0 требуют пристального внимания в любой программе бинарного поиска. В алгоритме 13.4 эти случаи обрабатываются тем же кодом, что и в общем случае, и поучительно посмотреть, как это делается, проследив за выполнением алгоритма для n =
2 и n = 1.


Корректность алгоритма 13.4 следует из утверждения, данного в комментарии в начале тела цикла. Он устанавливает, что если z находится где-либо в таблице, то оно должно находиться в интервале [l,h] ; иначе говоря, при нашем предположении, что имя появляется в таблице не больше одного раза, утверждается, что z не встречается ни в интервале [1,l - 1], ни в интервале [h + 1,n]. Это утверждение очевидно первый раз, когда мы входим в цикл при l = 1 и h = n, и непосредственно по индукции проверяется, что оно выполняется при каждом проходе через цикл. Когда мы выходим из цикла, то должно быть l > h, и поэтому утверждение принимает вид z \notin \{ x_1,\ldots,x_{l - 1} \} и z
\notin \{ x_{h + 1},\ldots,x_n \}, откуда следует, что z \notin \{
x_1,\ldots,x_n \}.

Оптимальные деревья бинарного поиска

Бинарный поиск в последовательно распределенной таблице ( алгоритм 13.4.) обеспечивает очень быстрое нахождение имен, которые являются средними точками на раннем этапе процесса деления пополам, именно имен, близких к вершине дерева T(1,n). Таким образом, любое имя в таблице можно выбрать примерно за \lg n сравнений.

Четыре дерева бинарного поиска над множеством имен {A,B,C,D}

Рис. 13.1. Четыре дерева бинарного поиска над множеством имен {A,B,C,D}

На практике в большинстве таблиц встречаются имена, к которым обращаются гораздо чаще, чем к другим, и "привилегированные" места в таблице разумно постараться использовать для наиболее часто вызываемых имен, а не для имен, выбранных для этих мест в результате бинарного поиска. Это невозможно осуществить для последовательно распределенных таблиц, поскольку место имени определяется его положением относительно естественного порядка имен в таблице. Введем структуру данных, легко приспосабливаемую как к месту бинарного поиска, так и к возможности выделять точки, в которых таблица делится на две части.

Деревом бинарного поиска над именами x_1,x_2,\ldots,x_n называется расширенное бинарное дерево, все внутренние узлы которого помечены различными именами из списка x_1,x_2,\ldots,x_n таким образом, что симметричный порядок узлов совпадает с естественным порядком. Каждый из n + 1 внешних узлов соответствует промежутку в таблице. На рис. 13.1 показаны четыре различных дерева бинарного поиска на множестве имен \{ A,B,C,D\}. Деревья (а) и (b) - вырожденные, поскольку они по существу являются линейными списками, которые должны просматриваться последовательно.

Поиск имени z в дереве бинарного поиска осуществляется путем сравнения z с именем, стоящим в корне. Тогда

  1. Если корня нет (дерево пусто), то z в таблице отсутствует и поиск завершается безуспешно.
  2. Если z совпадает с именем в корне, поиск завершается успешно.
  3. Если z предшествует имени в корне, поиск продолжается ниже в левом поддереве корня.
  4. Если z следует за именем в корне, поиск продолжается ниже в правом поддереве корня.

Пусть бинарное дерево имеет вид, в котором каждый узел представляет собой тройку (LEFT, NAME, RIGHT), где LEFT и RIGHTсодержат указатели левого и правого сыновей соответственно, и NAME содержит имя, хранящееся в узле. Указатели могут иметь значение \Lambda, означающее, что поддерево, на которое они указывают пусто. Если указатель корня дерева есть \Lambda, то само дерево пусто. Как и следовало ожидать, успешный поиск завершается во внутреннем узле дерева бинарного поиска и безуспешный поиск завершается во внешнем узле.

Эта процедура нахождения имени z в таблице, организованная в виде дерева бинарного поиска T, показана в алгоритме 13.5. Отметим его сходство с алгоритмом 13.4 (бинарный поиск).

Алгоритм 13.5. Поиск в дереве бинарного поиска

Алгоритм 13.5. Поиск в дереве бинарного поиска
< Лекция 12 || Лекция 13: 1234 || Лекция 14 >