Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие часть 1.doc
Скачиваний:
133
Добавлен:
24.09.2019
Размер:
6.98 Mб
Скачать

5.3. Бинарный поиск в упорядоченном массиве

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

Максимальное количество делений массива пополам при таком способе составляет ближайшее целое, большее log2n , следовательно, асимптотическая оценка сложности поиска O(logn). Например, если в массиве 1000 элементов, то за 10 делений пополам мы уменьшим массив до одного элемента. Фактически можно закончить поиск и раньше, если искомый элемент окажется на границе двух половин на каком-либо промежуточном шаге. Это настоящий прорыв в ускорении поиска.

Попробуем оценить, во что обходится поддержание массива в отсортированном виде. Ведь новый элемент уже нельзя будет вставить в конец массива, а удаляемый элемент нельзя заменить последним. Действительно, порядок временной сложности вставки становится O(n), время удаления также увеличивается, но асимптотическая оценка остается по-прежнему O(n) (сначала просто движемся по массиву вперед, чтобы найти нужный элемент, после этого продолжаем движение, перемещая каждый элемент влево).

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

В листинге 5.2 приведены два способа реализации бинарного поиска — итеративный и рекурсивный. На практике используется более эффективный итеративный вариант, но рекурсивное решение является хорошей иллюстрацией идеи бинарного поиска. Очевидно, уже понятно, что в основе ее лежит известный нам принцип «разделяй и властвуй».

Листинг 5.2. Бинарный поиск в отсортированном массиве

// рекурсивный вариант функции,

// начальный вызов: seach_bin_r(a, 0, n-1, k);

item seach_bin_r(item a[], int l, int r, T_key k)

{ int m=(l+r)/2;

if (a[m].key==k) return a[m];//

if (l==r) return nullitem;//

if (k<a[m].key) return seach_bin_r(a,l,m-1,k);

else return seach_bin_r(a,m+1,r,k);

}

// нерекурсивный вариант бинарного поиска

item seach_bin(item a[], int n, T_key k)

{ int l=0, r=n-1;

while (l<=r)

{ int m=(l+r)/2;

if (k>a[m].key) l=m+1;

else if (k<a[m].key) r=m-1;

else return a[m];

}

return nullitem;

}

5.4. Бинарные деревья поиска

Если проанализировать в целом реализацию поддержки поиска на основе линейной структуры, то можно сделать вывод, что любой вариант реализации включает функции с линейной временной сложностью алгоритма. Для обычного массива такой функцией является поиск (и удаление, если оно основано на поиске), для отсортированного — вставка и удаление. Если на больших совокупностях данных все эти операции выполняются достаточно часто, стоит подумать об отказе от линейной структуры в пользу иерархической. Для этого есть весомые основания — в древовидной структуре, в отличие от линейной, путь от корня к любым данным не превышает высоты дерева, следовательно, имеется реальная возможность вообще избавиться от медленных операций с линейной сложностью.

Бинарное дерево поиска уже упоминалось в предыдущей главе, посвященной сортировке. Напомним, что это упорядоченное бинарное дерево, у которого для каждого узла выполняется условие — все левые потомки имеют ключи, меньшие, чем ключ узла, а правые — большие (возможно равные).