Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекції на ОП та алг. мови.doc
Скачиваний:
13
Добавлен:
03.11.2018
Размер:
785.92 Кб
Скачать

2. Алгоритми

1. Лінійний пошук у масиві

Нехай таблиця представлена у виді масиву. Тоді перше, що приходить у голову з приводу пошуку елемента — це обхід всіх елементів, починаючи з першого, доти, поки не буде знайдений елемент із шуканим ключем, чи поки масив не скінчиться. Такий спосіб називається лінійним пошуком у неупорядкованому масиві. Оформимо його на Паскалі у виді процедури:

procedure LinearSearch(var T:tTable; k:tKey;var index:integer);

var i: integer;

begin

i:=1; index:=0;

while (i<=T.n)and(index=0) do begin

if T.a[i].key=k then index:=i;

i:=i+1;

end;

end;

Розглянемо докладніше частини цієї процедури. Параметрами процедури є таблиця (T), у якій потрібно шукати елемент, шукане значення ключа (k) і вихідний параметр (index), у якому процедура повинна вказати номер елемента, якщо він знайдений, і 0 у противному випадку. У списку параметрів таблиця T описана як параметр змінна, хоча процедура і не повинна змінювати які-небудь дані з таблиці. Це потрібно для того, щоб не створювати копію таблиці в стеку при передачі параметра процедурі, оскільки таблиця може мати великий розмір.

Можливий більш раціональний варіант: замість того щоб усякий раз перевіряти, чи не закінчився масив, можна використовувати масив з фіктивним елементом номер 0, перед пошуком записувати в нього шукане значення ключа, і рухатися по масиву від останнього елемента до першого. Такий спосіб називається лінійним пошуком з бар'єром (бар'єр — нульовий елемент):

procedure LinearSearch2(var T:tTable; k:tKey;var index:integer);

var i: integer;

begin

T.a[0]:=k;

index:=T.n; index:=0;

while T.a[index]<>k do index:=index-1;

end;

У такому варіанті стає значно менше порівнянь, отже, алгоритм працює швидше попереднього.

2. Двійковий пошук

Наступний алгоритм також застосовується для таблиці, представленої у виді масиву, крім того, масив повинний бути відсортованим за значеннями ключа (для визначеності — по зростанню). Тоді при пошуку можна використовувати наступні розуміння: візьмемо елемент, що знаходиться в середині масиву, якщо його ключ дорівнює шуканому ключу, то ми знайшли потрібний елемент, якщо менший — продовжуємо пошук у першій половині масиву, якщо більший — то в другий. Під продовженням розуміємо аналогічний процес: знову беремо середній елемент з обраної половини масиву, порівнюємо його ключ із шуканим ключем, і т.д. Цей цикл закінчиться, коли частина масиву, у якій виконується пошук, не буде містити жодного елемента. Тому що цей алгоритм багаторазово розбиває масив на дві частин, його називають алгоритмом двійкового пошуку. Нижче приведена відповідна процедура на Паскале.

procedure BinarySearch(var T:tTable; k:tKey; var index:integer);

var l,c,r: integer;

begin

index:=0;

l:=1; r:=T.n;

while (index=0)and(l<=r) do begin

c:=(l+r) div 2;

if T.a[c].key=k then index:=c

else if T.a[c].key>k then r:=c-1

else l:=c+1;

end;

end;

Змінні l, r і c позначають відповідно номер лівого краю, центра і правого краю частини масиву, у якій ми шукаємо елемент із заданим ключем. Пошук припиняється або якщо елемент знайдений (index <> 0), або якщо частина масиву, у якій потрібно шукати, була вичерпана (тобто номер лівого краю перевищив номер правого). Усередині циклу знаходимо номер середини частини масиву (c), потім порівнюємо ключ цього середнього елемента із шуканим ключем. Якщо виконалася рівність, то елемент знайдений, якщо середній більше шуканого, то встановлюємо праву границю частини масиву рівною c-1, якщо більше — змінюємо ліву границю на c+1.

3. Лінійний пошук у списку

Нехай тепер дані таблиці містяться в списку. У цьому випадку можна використовувати для пошуку алгоритм, дуже схожий на алгоритм лінійного пошуку в масиві. Відмінність лише в тім, що зміна номера поточного елемента заміняється переходом до покажчика на наступний елемент списку:

procedure SearchInList(T: tTable; k: tKey; var p: tItemPtr);

var notfound: boolean;

begin

notfound:=true;

p:=T;

while (p<>nil) and (notfound) do

begin

if p^.key=k then notfound:=false;

p:=p^.next;

end;

end;

Параметр T у цьому випадку не потрібно робити параметром-змінною, оскільки він є тільки покажчиком на початок таблиці, а сама таблиця лежить у динамічній пам'яті. Замість номера знайденого елемента будемо повертати користувачу нашої процедури покажчик на нього (якщо пошук був удалим) або nil, якщо пошук невдалий.

Скорочувати число перевірок у циклі за допомогою бар'єра було б нерозумно: щоразу бар'єр прийдеться ставити в «хвіст» списку, а для цього потрібно спочатку обійти весь список, починаючи з голови, затрачаючи при цьому багато часу.