
Бинарный (двоичный) поиск
Гораздо больший интерес представляют методы, не только работающие быстро, но и дающие лучшую теоретическую сложность. Один из таких методов – дихотомический поиск.
Этот метод поиска предполагает, что множество хранится, как некоторая отсортированная (например, по возрастанию) последовательность элементов, к которым можно получить прямой доступ посредством индекса. Фактически речь идет о том, что множество хранится в массиве и этот массив отсортирован.
Суть метода заключается в следующем. Сначала К сравнивается со средним ключом в таблице, результат сравнения позволяет определить в какой половине таблицы следует продолжить поиск, применяя к ней ту же процедуру и т.д.
Рассмотрим этот метод более подробно. Областью поиска (l, r) назовем часть массива с индексами от l до r, в которой предположительно находится искомый элемент.
Сначала областью поиска будет часть массива (l, r), где l=1, а r=n, то есть вся заполненная элементами множества часть массива.
Теперь найдем индекс среднего элемента m=(l+r) div 2.
Если Key>A[m], то можно утверждать (поскольку массив отсортирован), что если Key есть в массиве, то он находится в одном из элементов с индексами от m+l до r, следовательно, можно присвоить l=m+1, сократив область поиска.
В противном случае можно положить r=m. На этом заканчивается первый шаг метода. Остальные шаги аналогичны.
На каждом шаге метода область поиска будет сокращаться вдвое. Как только l станет равно r, то есть область поиска сократится до одного элемента, можно будет проверить этот элемент на равенство искомому и сделать вывод о результате поиска.
Бинарный поиск является методом непоследовательного поиска и известен также как метод половинного деления или дихотомии.
Функция Locate возвращает номер искомого ключа или N+1, если ключ не найден.
Function Locate( x:vector; k: integer): integer;
var l, r, i : integer;
begin
l:=1; r:=N;
repeat
i:=(L+r) div 2;
if K>x[i] then l:=i+1
else if K<x[i] then r:=i-1
until (K=x[i]) or (l>r);
if K=x[i] then Locate := i else Locate:=N+1
end;
Бинарный поиск, никогда не использует более чем log2N+1 сравнений как для успешного, так и для неуспешного поиска. А это означает сложность T(log(n)).
Это свойство следует из того, что количество обрабатываемых записей в каждом цикле сокращается вдвое. Количество производимых при этом сравнений равно CN= CN/2+1 при C1= 1, что доказывает данное свойство.
Добавление граничного элемента дает усовершенствованный вариант алгоритма:
Function Locate( x:vector; k: integer): integer;
var l, r, i : integer;
Begin
l:=1; r:=N; x[N+1]:=K;
repeat
i:=(L+r) div 2;
if K>x[i] then l:=i+1
else if K<x[i] then r:=i-1
until K=x[i];
Locate:=i
end;
Интерполяционный поиск
Рассмотрим еще один метод, имеющий хорошую теоретическую сложность. Как и предыдущий, этот метод предполагает, что множество хранится в массиве и массив отсортирован. Как и предыдущий метод, этот на каждом шаге своей работы сокращает область поиска. Но в отличие от предыдущего метода проба берется не в середине рассматриваемой области.
Пусть имеется область поиска (l, r). Предположим, что элементы множества – целые числа, возрастающие в арифметической прогрессии. Тогда искомый элемент должен находиться в массиве (если он вообще там есть) под индексом:
.
Это следует из свойств арифметической прогрессии. В этом элементе и будем брать пробу.
l := 1; r := n;
while (l<>r) do
begin
m := l+(r-l)*(Key-A[l])/(A[r]-A[l]);
if Key>A[m] then l := m+1 else r := m;
end;
if A[l]=Key then <элемент найден> else <элемент не найден>;
Мы сделали очень большое допущение, предположив, что элементы массива представляют собой возрастающую арифметическую прогрессию. В реальности такая ситуация встречается редко. Но этот метод хорошо работает для любых пусть не идеально, но более-менее равномерно распределенных данных.
Если же мы имеем дело с неравномерно распределенными данными, то интерполяционный поиск может увеличить число шагов по сравнению с дихотомическим поиском.
Теоретическая сложность интерполяционного поиска – T(log(log(n))). Это, конечно, лучше, чем сложность дихотомического поиска, но эти преимущества становятся достаточно заметными лишь при очень больших значениях n. Практически на всех реальных n разница между дихотомическим и интерполяционным поиском по скорости не существенна.
Контрольные вопросы
Почему последний из разобранных методов поиска называется интерполяционным?
Почему при хранении в списке невозможно воспользоваться дихотомическим и интерполяционным поиском?