Лінійний пошук. Розробка класу, що реалізує лінійний пошук в одновимірному масиві
У даній темі розглядається реалізація лінійного пошуку елементу в одновимірному масиві. З демонстраційною метою розроблено клас LSearch<T>, який містить засоби для організації лінійного пошуку.
Зміст
- 1. Лінійний пошук. Поняття
- 2. Розробка шаблонного класу LSearch<T>, який реалізує лінійний пошук
- 2.1. Умова задачі
- 2.2. Загальна структура класу. Внутрішні змінні
- 2.3. Конструктори класу
- 2.4. Методи доступу до екземпляру масиву
- 2.5. Деструктор
- 2.6. Операторна функція operator=()
- 2.7. Методи реалізації лінійного пошуку
- 2.8. Метод Print(). Виведення вмісту масиву на екран
- 2.9. Функція main(). Тестування роботи класу
- 2.10. Текст класу (скорочений варіант)
- Зв’язані теми
Пошук на інших ресурсах:
1. Лінійний пошук. Поняття
Лінійний пошук належить до найбільш простих способів пошуку даних. Мета лінійного пошуку – знайти потрібний елемент (ключ) в масиві даних. В алгоритмі лінійного пошуку кожен елемент масиву послідовно порівнюється з ключем до тих пір, поки не буде знайдено потрібний елемента або не буде проскановано увесь масив.
В контексті лінійного пошуку можуть виникати наступні задачі:
- визначити наявність заданого елементу в масиві (наборі даних);
- визначити кількість входжень заданого елементу в масиві;
- визначити номер позиції або масив номерів позицій розміщення заданого елементу в масиві.
Реалізація лінійного пошуку може бути розширена для використання в багатовимірних масивах.
⇑
2. Розробка шаблонного класу LSearch<T>, який реалізує лінійний пошук
Шаблонний клас, що реалізує лінійний пошук, може бути розміщений в окремому модулі (файлі) і використовуватись при необхідності. Розроблений клас може бути використаний для базових типів мови C++ таких як int, double, char та інших.
Якщо виникне необхідність використати даний клас для масиву екземплярів іншого класу, то в цьому (іншому) класі потрібно перевизначити операцію порівняння (==), яка порівнює два екземпляри класу на рівність. Але це вже інша тема.
2.1. Умова задачі
Розробити клас, який містить засоби для виконання лінійного пошуку в одновимірному масиві. У класі потрібно реалізувати наступні поля та методи:
- внутрішні змінні, які описують масив;
- усі необхідні конструктори класу;
- методи доступу до масиву в класі;
- деструктор;
- операторну функцію operator=(), яка перевантажує оператор присвоєння;
- методи реалізації лінійного пошуку як в масиві класу, так і в зовнішньому масиві.
У функції main() продемонструвати роботу класу.
⇑
2.2. Загальна структура класу. Внутрішні змінні
Оскільки лінійний пошук реалізується в одновимірному масиві, то внутрішніми даними класу є одновимірний масив A та його розмірність count.
Клас є шаблонним і працює з узагальненим типом T. Після вводу внутрішніх змінних загальний вигляд класу для додатку типу Console Application наступний
#include <iostream> using namespace std; // Шаблонний клас, що реалізує лінійний пошук в одновимірному масиві template <class T> class LSearch { private: // Внутрішні змінні T* A; // масив int count; // кількість елементів у масиві } void main(void) { }
⇑
2.3. Конструктори класу
У тіло класу в розділі public вписуються коди конструкторів класу.
2.3.1. Конструктор без параметрів
Конструктор без параметрів встановлює внутрішні змінні нульовими значеннями. Реалізація такого конструктору передбачає:
- додавання масиву даних з допомогою методу Set() та його обробку;
- використання методів лінійного пошуку для масивів, що не є даними поточного екземпляру класу (для будь-яких інших зовнішніх масивів).
Після додавання конструктора в розділ public загальний вигляд класу наступний
// Шаблонний клас, що реалізує лінійний пошук в одновимірному масиві template <class T> class LSearch { private: // Внутрішні змінні T* A; // масив int count; // кількість елементів у масиві public: // Конструктор без параметрів LSearch() { A = nullptr; count = 0; } }
⇑
2.3.2. Конструктор, що ініціалізує внутрішні поля класу вхідним масивом
Даний конструктор є необхідний, якщо в програмі вже є зовнішній масив з даними. Конструктор робить копіювання зовнішнього масиву у внутрішній для його подальшої обробки.
// Конструктор, який отримує вхідний масив, // яким ініціалізуються елементи внутрішнього масиву LSearch(T* _A, int _count) { // 1. Перевірка, чи коректне значення _count if (_count <= 0) { A = nullptr; count = 0; return; } // 2. Встановити нову кількість елементів count = _count; // 3. Виділити пам'ять для масиву A try { A = new T[count]; } catch (bad_alloc e) { cout << e.what() << endl; return; } // 4. Скопіювати A = _A for (int i = 0; i < count; i++) A[i] = _A[i]; }
⇑
2.3.3. Конструктор копіювання LSearch(const LSearch&)
Оскільки пам’ять в масиві класу виділяється динамічно, то необхідно оголосити конструктор копіювання. Більш детально про необхідність використання констуктора копіювання та оператора присвоєння в класі описується тут.
// Конструктор копіювання - перенаправлення на конструктор з двома параметрами LSearch(const LSearch& _A) :LSearch(_A.A, _A.count) { }
⇑
2.4. Методи доступу до екземпляру масиву
Для доступу до внутрішнього масиву класу реалізовано два методи Get(), Set(). Ці методи розміщуються в розділі public.
2.4.1. Метод Get(). Отримати покажчик на внутрішній масив
З допомогою методу Get() можна отримати покажчик на внутрішній масив A класу. Метод отримує посилання на зовнішню змінну, що отримає значення кількості елементів масиву A.
// Метод, що повертає покажчик на внутрішній масив A класу T* Get(int* _count) { *_count = count; return A; }
⇑
2.4.2. Метод Set(). Встановити нові значення в масиві
Метод Set() дозволяє встановити новий масив в якості оброблюваного.
// Метод зміни масиву в поточному екземплярі void Set(T* _A, int _count) { // 1. Перевірка, чи коректне значення _count if (_count <= 0) { A = nullptr; count = 0; return; } // 2. Звільнити пам'ять, виділену для масиву A поточного екземпляру if (count > 0) delete[] A; // 3. Встановити нову кількість елементів count = _count; // 4. Виділити новий фрагменту пам'яті для A try { A = new T[count]; } catch (bad_alloc e) { cout << e.what() << endl; return; } // 5. Присвоїти A = _A for (int i = 0; i < count; i++) A[i] = _A[i]; }
⇑
2.5. Деструктор
Деструктор звільняє пам’ять, зайняту внутрішнім масивом A. Деструктор викликається автоматично в момент знищення екземпляру класу.
// Деструктор ~LSearch() { if (count > 0) { delete[] A; } }
⇑
2.6. Операторна функція operator=()
У класі потрібно перевизначити оператор присвоєння =, який здійснює присвоєння екземплярів класу LSearch<T>. Це необхідно для уникнення недоліків побітового копіювання, яке виникає у випадку, коли в класі використовуються покажчики. Більш детально про необхідність перевизначення оператора присвоєння в класі описується тут.
// Перевизначений оператор присвоєння LSearch& operator=(LSearch& _A) { // Викликати метод Set() класу Set(_A.A, _A.count); // Повернути поточний екземпляр return *this; }
⇑
2.7. Методи реалізації лінійного пошуку
2.7.1. Метод IsItem(). Визначення, чи є елемент (ключ) в масиві
Метод IsItem() визначає, чи є елемент в масиві. Метод здійснює послідовне сканування масиву з початку до кінця. Результатом роботи методу є значення true або false.
У класі метод має дві перевантажені реалізації. Перша реалізація призначена для будь-якого масиву. Друга реалізація застосовується для внутрішнього масиву поточного екземпляру класу.
// Перевантажений метод IsItem() // Визначення, чи є елемент key в масиві, який заданий вхідним параметром bool IsItem(T* _A, int _count, T key) { for (int i = 0; i < _count; i++) if (_A[i] == key) // як тільки знайдено елемент, то вихід з функції return true; return false; } // Визначення, чи є елемент key в масиві, який заданий в екземплярі класу bool IsItem(T key) { // викликати загальний метод з даними поточного екземпляру return IsItem(this->A, this->count, key); }
⇑
2.7.2. Метод NumOccurs(). Отримати кількість входжень елементу в масиві
Метод NumOccurs() дозволяє отримати кількість входжень заданого ключа у масиві. Метод має дві перевантажені реалізації: для зовнішнього масиву та для внутрішнього масиву класу.
// 2. Перевантажений метод NumOccurs() // Обчислити кількість входжень елементу в масив, який заданий вхідним параметром int NumOccurs(T* _A, int _count, T key) { int num = 0; for (int i = 0; i < _count; i++) if (_A[i] == key) num++; return num; } // Обчислити кількість входжень елементу в масив, який заданий // в поточному екземплярі класу int NumOccurs(T key) { // Викликати однойменний метод з поточними даними return NumOccurs(A, count, key); }
⇑
2.7.3. Метод GetPos(). Отримати масив позицій входжень елементу в масиві
Метод GetPos() повертає масив позицій входжень заданого ключа в масиві. Всередині методу GetPos() виділяється пам’ять для результуючого масиву. Тому, при використанні методу, потрібно попіклуватись про звільнення цієї пам’яті.
// 3. Перевантажений метод GetPos() // Повернути масив позицій входження заданого елементу у вхідному масиві int* GetPos(T* _A, int _count, T key, int& nPos) { // 1. Перевірка, чи коректне значення count if (_count <= 0) { return nullptr; } // 2. Оголосити внутрішні змінні int* P = nullptr; // Результуючий масив // 3. Обчислити кількість входжень nPos nPos = NumOccurs(_A, _count, key); // 4. Виділити пам'ять для результуючого масиву P try { P = new T[nPos]; } catch (bad_alloc e) { cout << e.what() << endl; return nullptr; } // 5. Цикл обчислення номерів позицій // та формування масиву P int t = 0; for (int i = 0; i < _count; i++) if (_A[i] == key) P[t++] = i; // 6. Повернути покажчик на масив позицій return P; } // Повернути масив позицій у масиві поточного екземпляру int* GetPos(T key, int& nPos) { // Викликати однойменну функцію return GetPos(A, count, key, nPos); }
⇑
2.8. Метод Print(). Виведення вмісту масиву на екран
Метод Print() реалізований для консольного додатку і виводить значення елементів масиву на екран.
// Метод, що виводить дані масиву екземпляру на екран з коментарієм text void Print(const char* text) { cout << text << endl; cout << "count = " << count << endl; for (int i = 0; i < count; i++) cout << A[i] << " "; cout << endl; }
⇑
2.9. Функція main(). Тестування роботи класу
Тестування роботи класу здійснюється у функції main() для консольного додатку (тип Console Application).
void main(void) { // Продемонструвати роботу класу LSearch // Екземпляр масиву типу int - викликається конструктор без параметрів LSearch<int> L1; // Досліджуваний масив чисел типу int int A[] = { 0, 2, 3, 5, 7, 3, 4, 8, 4, 0, 0, 2 }; int count = 12; // к-сть елементів у масиві A // Додати масив A до екземпляру L1 L1.Set(A, count); // перевірка методу Set // Визначити, чи взагалі є елемент 3 if (L1.IsItem(3)) { // Якщо є, то вивести кількість входжень елементу 3 в масиві A int k = L1.NumOccurs(3); cout << "k = " << k << endl; } L1.Print("L1:"); // Перевірка конструктора копіювання LSearch<int> L2 = L1; L2.Print("L2:"); // Перевірка оператора присвоєння LSearch<int> L3; L3 = L1; L3.Print("L3:"); // Перевірка методу Get() - заповнити новий масив значеннями int* A2; int count2; A2 = L3.Get(&count2); // Вивести масив A2 cout << "A2.count = " << count2 << endl; for (int i = 0; i < count2; i++) cout << A2[i] << " "; cout << endl; // Перевірка методу GetPos() - отримати масив позицій входження int nPos; int* P = L1.GetPos(0, nPos); cout << "nPos = " << nPos << endl; for (int i = 0; i < nPos; i++) cout << P[i] << " "; cout << endl; // Після використання, потрібно звільнити пам'ять для масиву P if (P != nullptr) delete[] P; // Перевірка конструктора, що отримує параметром вхідний масив типу double double B[] = { 3.1, 2.8, 0.85, -10.5 }; // масив даних LSearch<double> L4(B, 4); // викликається конструктор з двома параметрами L4.Print("L4"); // Визначити кількість входжень числа 0.85 у масиві int k = L4.NumOccurs(0.85); cout << "k = " << k << endl; }
Результат роботи програми
k = 2 L1: count = 12 0 2 3 5 7 3 4 8 4 0 0 2 L2: count = 12 0 2 3 5 7 3 4 8 4 0 0 2 L3: count = 12 0 2 3 5 7 3 4 8 4 0 0 2 A2.count = 12 0 2 3 5 7 3 4 8 4 0 0 2 nPos = 3 0 9 10 L4 count = 4 3.1 2.8 0.85 -10.5 k = 1
⇑
2.10. Текст класу (скорочений варіант)
Послідовно виконавши усі попередні пункти можна отримати повний текст класу LSearch<T>. Нижче наведено програмний код класу в скороченому вигляді.
#include <iostream> using namespace std; // Шаблонний клас, що реалізує лінійний пошук в одновимірному масиві template <class T> class LSearch { private: // Внутрішні змінні T* A; // масив int count; // кількість елементів у масиві public: // Конструктори // Конструктор без параметрів LSearch() { ... } // Конструктор, який отримує вхідний масив, // яким ініціалізуються елементи внутрішнього масиву LSearch(T* _A, int _count) { ... } // Конструктор копіювання - перенаправлення на конструктор з двома параметрами LSearch(const LSearch& _A) :LSearch(_A.A, _A.count) { } // ------- Методи доступу до екземпляру масиву ------------- // Метод зміни масиву в поточному екземплярі void Set(T* _A, int _count) { ... } // Метод, що повертає покажчик на внутрішній масив класу T* Get(int* _count) { ... } // Деструктор ~LSearch() { ... } // Перевизначений оператор присвоєння LSearch& operator=(LSearch& _A) { ... } // ------ Методи пошуку ---------- // 1. Перевантажений метод IsItem() // Визначення, чи є елемент key в масиві, який заданий вхідним параметром bool IsItem(T* _A, int _count, T key) { ... } // Визначення, чи є елемент key в масиві, який заданий в екземплярі класу bool IsItem(T key) { ... } // 2. Перевантажений метод NumOccurs() // Обчислити кількість входжень елементу в масив, який заданий вхідним параметром int NumOccurs(T* _A, int _count, T key) { ... } // Обчислити кількість входжень елементу в масив, який заданий // в поточному екземплярі класу int NumOccurs(T key) { ... } // 3. Перевантажений метод GetPos() // Повернути масив позицій входження заданого елементу у вхідному масиві int* GetPos(T* _A, int _count, T key, int& nPos) { ... } // Повернути масив позицій у масиві поточного екземпляру int* GetPos(T key, int& nPos) { ... } // Метод, що виводить дані масиву екземпляру на екран з коментарієм text void Print(const char* text) { ... } }; void main(void) { ... }
⇑
Зв’язані теми
- Розробка класу, що реалізує масив рядків типу char*
- Розробка класу Random для генерування випадкових чисел
⇑