Линейный поиск. Разработка класса реализующего линейный поиск в одномерном массиве
В данной теме рассматривается реализация линейного поиска элемента в одномерном массиве. С целью демонстрации разработан класс 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 генерирования случайных чисел
⇑