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

12 Пошук

12.1 Алгоритми пошуку

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

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

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

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

В прикладі 12.1 подана програма, яка виконує пошук відомостей про підприємство Київтрасгаз у масиві таблиці 11.1. Тут пошук виконує підпрограма p(). Вона має всього один цикл типу for з параметром і, який змінюється від 0 до n-1, де n – кількість елементів масиву. Порівняння елементів у циклі виконує стандартна функція strcmp(), яка видає нуль у випадку входження другого свого параметра в перший (тобто, якщо рядок Київтрасгаз входить до складу рядка a[i].naz). Функція повертає число i (в головній програмі це число k) – номер індекса в масиві m[6].

Приклад 12.1 – Послідовний пошук елемента масиву

# include<stdio.h> /* Послідовний пошук одного елемента масиву */

# include<string.h>

struct pidpr{int kod;

char naz[25];

float stf;};

int p(struct pidpr *a, char *slo, int n)

{

int i;

for(i = 0; i<n; i++)if( ! strcmp(a[i].naz, slo))break;

return i;

}

main( )

{int count = 6, k;

char slovo[25]="Київтрансгаз";

struct pidpr m[6] = {{1, "Харківтрансгаз ", 34.60},

{2, "Прикарпаттрансгаз ", 22.15},

{3, "Київтрансгаз ", 75.00},

{4, "Львівтрансгаз ", 42.12},

{5, "Експорттрансгаз ", 1.85},

{6, "Гадячгазпром ", 12.48}};

clrscr( );

k=p(m, slovo, count);

if(k<count)printf("\n k=%d kod = %d naz = %s stf = %5.2f\n",

k, m[k].kod, m[k].naz, m[k].stf);

else puts(" Об’єкт не знайдений.");

getch( );

}

k=2 kod = 3 naz = Київтрансгаз stf =75.00

Метод половинного ділення ще називають методом дихотомії. Він призначений для пошуку в посортованому масиві.

Суть цього методу полягає в тому, що в циклі вибирається середній елемент масиву, який ділить його на дві частини: ліву й праву. Якщо він менший за відшукуваний, то для подальшого ділення вибирається права половина, інакше – ліва, тому на кожному кроці область пошуку скорочується вдвоє. Пошук припиняється, коли область пошуку скоротиться до одного елемента, який і буде відшукуваним. При відсутності пошукуваного елемента буде знайдений найближчий до нього.

Цей метод дозволяє суттєво зменшити кількість порівнянь. Його ефективність зростає із збільшенням кількості елементів масиву, чим довший масив, тим метод ефективніший. Кількість порівнянь цього методу дорівнює log2(n). У реальних умовах виробництва якраз найчастіше приходиться обробляти великі масиви даних, що й зумовило широке його застосування. Однак, він працює лише в посортованому масиві.

Нижче в прикладі 12.2 поданий варіант функції p(), яка розв’язує ту ж задачу, що і в прикладі 12.1, лише застосовується метод дихотомії. Зрозуміло, що тоді масив структур m[6] повинен бути посортованим.

Приклад 12.2 – Метод половинного ділення

int p(struct pidpr *a, char *slo, int n)

{

int i, k= 0;

while(1)

{

i =ceil((n+k)/2.);

if(i == k || i == n)break; if(!strcmp(nazva[k], slo))return k;

if(strcmp(a[i].naz, slo)>=0) n = i; else k = i;

}

return i;

}

У програмі прикладу 12.2 використовуються змінні: k – ліва, n – права межа та i – середина області пошуку. Змінна i закруглюється до найближчого цілого в сторону збільшення, інакше непарні елементи масиву можуть випадати з області пошуку. Однак, внаслідок цього заокруглення з області пошуку буде випадати нульовий елемент, тому тут додано оператор if( !strcmp(nazva[k], slo))return k. Заокруглення виконує функція ceil(), яка, нагадаємо, вимагає директиви препроцесору # inlude<math.h>.

12.2 Використання допоміжного масиву структур для пошуку

Під час виконання пошукових операцій часто приходиться мати декілька по-різному посортованих структур або зв’язаних списків – за різними умовами. Зокрема, у прикладі 11.6 можна сортувати структури в порядку зростання значень змінних kod та stf або за розташуванням назв підприємств у алфавітному порядку. Отже, для застосування методу дихотомії прийдеться або мати декілька копій масиву структур, або кожного разу їх пересортовувати. В цьому випадку вигідніше виготовити потрібну кількість допоміжних копій, кожна з яких міститиме лише посортовані значення елемента і відповідні їм значення індексів еталонного масиву.

Кожну таку копію можна представити у вигляді масиву структур з двома елементами: значення, серед яких відбувається пошук, і відповідні їм індекси у еталонному масиві. Тоді під час пошуку заданого значення користуються допоміжним масивом структур, а решту даних беруть з еталонного масиву за відповідним індексом цього значення у копії. Допоміжний масив подібний до індекса бази даних.

Виготовлення допоміжного масиву структур показано в прикладі 12.3. Його застосуємо для поставленої в прикладі 12.1 задачі –пошуку відомостей про підприємство Київтрасгаз. У нашому прикладі допоміжний масив структур має назву dv[6]. Структура містить назву підприємства nazva[25] літерного типу та відповідний їй індекс index цілого типу.

Сортування методом вибору забезпечує функція s(). Вона подібна до вже відомої з прикладу 11.6. Починається ця функція з копіювання у масив dv[6] назв підприємств та їхніх порядкових номерів із заданого масиву структур a[ ].

Приклад 12.3 – Використання допоміжного масиву структур

# include<stdio.h> /* Пошук у допоміжному масиві */

# include<string.h>

# include<math.h> /* Для функції ceil() */

struct pidpr{int kod;

char naz[25];

float stf;};

struct{ char nazva[25];

int index;

}dv[6];

char min[25]; int ind;

/* Створення допоміжного масиву структур dv[ ] (сортування) */

void s(struct pidpr *a, int n)

{

int i, j;

for(i = 0; i<n; i++){strcpy(dv[i].nazva, a[i].naz); dv[i].index = i;}

for(i = 0; i<n; i++)

{

for(j = i+1; j<n; j++)

if(strcmp(dv[j].nazva, dv[i].nazva)<0)

{strcpy(min, dv[j].nazva); ind= dv[j].index;

strcpy(dv[j].nazva, dv[i].nazva); dv[j].index= dv[i].index;

strcpy(dv[i].nazva,min); dv[i].index=ind;

}

}

return;

}

/* Пошук елемента у допоміжному масиві структур dv[ ]*/

int p(char *slo, int n)

{

int i, k= 0;

while(1)

{

i =ceil((n+k)/2.);

if(i==k || i== n)break; if( !strcmp(dv[k].nazva, slo))return dv[k].index;

if(strcmp(dv[i].nazva, slo)>=0)n = i; else k = i;

}

return dv[i].index;

}

/* Головна програма */

int main(void)

{int i, count = 6, w;

char slovo[25]="Київтрансгаз";

struct pidpr m[6] = {{1, "Харківтрансгаз ", 34.60},

{2, "Прикарпаттрансгаз ", 22.15},

{3, " Київтрансгаз ", 75.00},

{4, "Львівтрансгаз ", 42.12},

{5, "Експорттрансгаз ", 1.85},

{6, "Гадячгазпром ", 12.48}};

s(m, count);

puts("\n Результати виконання програми\nДопоміжний масив структур:");

for(i = 0; i<count; i++)printf("%s %d\n", dv[i].nazva, dv[i].index);

w=p(slovo, count);

puts("\n Знайдені відомості про підприємство:");

printf("kod=%d naz=%s stf=%5.2f\n", m[w].kod,m[w].naz,m[w].stf);

getch( );

return 0;

}

Результати виконання програми

Допоміжний масив структур:

5 Гадячгазпром

4 Експорттрансгаз

2 Київтрансгаз

3 Львівтрансгаз

1 Прикарпаттрансгаз

0 Харківтрансгаз

Знайдені відомості про підприємство:

kod=3 naz=Київтрансгаз stf=75.00

Пошук за допомогою допоміжної структури можна виконати будь-яким відомим способом, але, зрозуміло, що пріоритетним буде метод половинного ділення. Нехай поставимо та ж задачу, що і в прикладі 12.1 – знайти відомості про підприємство Київтрасгаз. Пошук виконує функція p(), яка подібна до прикладу 12.2, але відрізняється від нього тим, що видає не значення простої змінної, а елемента масиву dv[i].index.

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

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

Запитання для самоперевірки

  1. Перелічіть та коротко охарактеризуйте подані тут методи пошуку.

  2. Скільки операцій порівняння потрібно затратити для пошуку найменшого числа в масиві, посортованому в порядку зростання?

  3. Як можна знайти значення близьке до пошукуваного із застосуванням послідовного пошуку? методу дихотомії?

  4. Вкажіть переваги та недоліки методу дихотомії.

  5. Як виконується пошук у зв’язаному списку? Чи можна тут застосувати метод дихотомії?

  6. Чим відрізняється сортування допоміжного масиву структур від масивів інших структур?

  7. Використовуючи функцію clock(), відому з розділу 7, знайдіть ту межу, коли застосування методу дихотомії та допоміжного масиву структур стане невигідним.

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