Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Strukturi_danikh_ta_algoritmi_-_konspekt_lektsi...doc
Скачиваний:
33
Добавлен:
23.11.2019
Размер:
870.4 Кб
Скачать

3.2.Бінарний пошук

Очевидно, що інших способів пришвидшення пошуку не існує, якщо, звичайно, немає ще якої-небудь інформації про дані, серед яких ведеться пошук. Алгоритм пошук може бути значно ефективнішим, якщо дані будуть впорядковані.

Іншим, відносно простим, методом доступу до елемента є метод бінарного (дихотомічного) пошуку, який виконується в явно впорядкованій послідовності елементів. Записи в таблицю заносяться в лексикографічному (символьні ключі) або чисельно (числові ключі) зростаючому порядку. Для досягнення впорядкованості може бути використаний котрийсь з методів сортування, які розглянемо пізніше.

Оскільки шуканий елемент швидше за все знаходиться „десь в середині”, перевіримо саме середній елемент: a[N / 2] == key? Якщо це так, то знайдено те, що потрібно. Якщо a[N / 2] < key , то значення i = N / 2 є замалим і шуканий елемент знаходиться „праворуч”, а якщо a[N / 2] > key, то „ліворуч”, тобто на позиціях 0 … i.

Для того, щоб знайти потрібний запис в таблиці, у гіршому випадку потрібно log2(N) порівнянь. Це значно краще, ніж при послідовному пошуку.

Приведемо ілюстрація бінарного пошуку на прикладі.

int BinSearch(int *a, int key)

{

int b, e, i;

b = 0; e = N-1; // початкові значення меж

bool Found = false; // прапорець

while ( (b < e) && !Found) // цикл, поки інтервал пошуку не звузиться до 0

{

i = ( b + e ) / 2; // середина інтервалу

if ( a[i] == key )

Found = true; // ключ знайдений

else

if ( a[i] < key )

b = i + 1; // пошук в правому підінтервалі

else

e = i - 1; // пошук в лівому підінтервалі

}

return i;

}

Максимальна кількість порівнянь для цього алгоритму рівна log2(N). Таким чином, приведений алгоритм суттєво виграє у порівнянні з лінійним пошуком.

Ефективність дещо покращиться, якщо поміняти місцями заголовки умовних операторів. Перевірку на рівність можна виконувати в другу чергу, так як вона зустрічається лише одноразово і приводить до завершення роботи. Але більш суттєвий виграш дасть відмова від завершення пошуку при фіксації знаходження елемента.

int BinSearch(int *a, int key)

{

int b, e, i;

b = 0; e = N-1;

while (b<e)

{

i = (b + e) / 2;

if (а[m] < x)

b = i + 1;

else

e = i - 1;

}

return i

}

Завершення циклу гарантовано. Це пояснюється наступним. На початку кожного кроку b<e. Для середнього арифметичного i справедлива умова b<=i<e. Значить, різниця e-b дійсно спадає, тому що або b збільшується при присвоєнні йому значення i+1, або e зменшується при присвоєнні йому значення i-1. При b<=i повторення циклу закінчується.

Виконання умови b=e ще не засвідчує знаходження потрібного елемента. Тут потрібна додаткова перевірка. Також, необхідно враховувати, що елемент a[e] у порівняннях ніколи не бере участі. Значить, і тут необхідна додаткова перевірка на рівність a[e]=key. Але ці перевірки виконуються однократно.

Алгоритм бінарного пошуку можна представити і трохи інакше, використовуючи рекурсивний опис. В цьому випадку граничні індекси інтервалу b і e є параметрами алгоритму. Рекурсивна процедура бінарного пошуку представлена в наступній програмі. Для виконання пошуку необхідно при виклику процедури задати значення її формальних параметрів b і е0 і N-1 відповідно, де b, e – граничні індекси області пошуку.

int BinSearch(int *a, int key, int & b, int & e)

{

int i;

if ( b > e )

return -1; // перевірка ширини інтервалу

else

{

i = ( b + e ) / 2; // середина інтервалу

if ( a[i] == key )

return i; // ключ знайдений, повернення індексу

else

if ( a[i] < key ) // пошук в правому підінтервалі

return BinSearch(a, key, i+1, e);

else // пошук в лівому підінтервалі

return BinSearch(a, key, b, i-1);

}

}

Відомо, також, декілька модифікацій алгоритму бінарного пошуку, які виконуються на деревах.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]