8 - Разработка шаблонного стека и использование STL для обработки данных

Лабораторная работа 8 для студентов курса 2 семестра кафедры ИУ5 МГТУ им Н.Э. Баумана.

Содержание

Цель работы

Начало работы

Зайдите в свою локальную директорию с репозиторием для выполнения лабораторных работ. Заберите ветку с соответствующей лабораторной работой из общего репозитория:

git pull upstream

или

git pull upstream lab_8

Переключитесь на ветку с текущей лабораторной работой:

git checkout lab_8

Свяжите ветку локального репозитория с вашим удаленным репозиторием:

git push --set-upstream origin lab_8

Задание

Задание 1. Реализация шаблонного класса стека

1.1 Структура ListNode

Разработайте структуру ListNode, представляющую узел связного списка:

template <typename T>
struct ListNode {
    T data;
    ListNode<T>* next;
    ListNode(const T& value) : data(value), next(nullptr) {}
};

1.2 Класс MyStack

Разработайте шаблонный класс MyStack, реализующий стек на основе односвязного списка.

private:
  ListNode<T>* top; //  указатель на вершину стека.

public:
  MyStack();                   // конструктор по умолчанию.
  ~MyStack();                  // деструктор (освобождает память).
  void push(const T& value);   // добавляет элемент на вершину.
  bool pop(T& value);          // удаляет элемент с вершины, возвращает его значение.
  bool peek(T& value) const;   // возвращает значение верхнего элемента без удаления.
  bool isEmpty() const;        // проверка на пустоту.
  int size() const;            // возвращает количество элементов.
  void clear();                // очищает стек.
  void print() const;          // выводит содержимое стека (от вершины ко дну).

Требования:

Задание 2. Разложение числа на простые множители с использованием стека

Разработайте функцию:

void Multipliers(int n, MyStack<int>& stack);

Функция должна раскладывать число n на простые множители и помещать их в стек (в порядке нахождения, начиная с наименьшего делителя).

В main():

Пример вывода:

3960 = 11 * 5 * 3 * 3 * 2 * 2 * 2
3960 = 2 * 2 * 2 * 3 * 3 * 5 * 11

Примечание:

Стек является структурой LIFO (Last In – First Out), поэтому порядок вывода элементов зависит от способа извлечения.

Задание 3. Перенос данных из стека в контейнеры STL и их обработка

После выполнения задания 2 в стеке MyStack<int> содержатся множители числа 3960. Используйте этот стек как единственный источник данных для выполнения следующих подзаданий.

3.1 Перенос в vector и использование алгоритмов

3.1.1. Создайте std::vector<int> и перенесите в него все элементы из стека в порядке от дна к вершине (то есть в возрастающем порядке множителей: 2, 2, 2, 3, 3, 5, 11).

3.1.2. Используя алгоритмы STL и лямбда-выражения, выполните:

3.1.3. Для сортировки по убыванию продемонстрируйте использование функтора (отдельный класс с перегруженным operator()).

3.2 Перенос в list и использование алгоритмов

3.2.1. Создайте std::list<int> и перенесите в него все элементы из исходного стека (также в возрастающем порядке).
3.2.2. Используя алгоритмы STL, выполните:

3.3 Перенос в map и использование алгоритмов

Контейнер std::map хранит элементы в отсортированном виде по ключу.

3.3.1. Создайте std::map<int, int>, где ключ — это множитель, а значение — количество его вхождений. Заполните отображение, анализируя данные из исходного стека.

3.3.2. Используя алгоритмы STL, выполните:

3.4. Использование алгоритмов для работы с несколькими контейнерами

3.4.1. Создайте два вектора: один с исходными множителями, второй — с их квадратами (из п. 3.1.2).

3.4.2. Используя алгоритм std::inner_product, вычислите скалярное произведение этих двух векторов.

3.4.3. Используя std::transform и лямбду, создайте третий вектор, содержащий суммы соответствующих элементов первых двух векторов.

Задание 4. Дополнительные задания (по выбору)

Выполняется для получения повышенной оценки.

4.1 Реализуйте функтор для сравнения элементов при сортировке по убыванию (использовать в std::sort вместо лямбды).

4.2 Напишите шаблонную функцию void printContainer(const Container& cont), которая выводит содержимое любого контейнера STL (vector, list, map) с использованием итераторов и лямбды.

4.3 Используйте std::accumulate для вычисления произведения всех множителей (должно получиться исходное число 3960).

Требования к оформлению

  1. Весь код должен быть размещён в файлах:
========== ЗАДАНИЕ 2: Разложение на множители ==========
3960 = 11 * 5 * 3 * 3 * 2 * 2 * 2
3960 = 2 * 2 * 2 * 3 * 3 * 5 * 11

========== ЗАДАНИЕ 3.1: Работа с vector ==========
Исходный вектор (возрастающий): 2 2 2 3 3 5 11
Вектор после сортировки по убыванию: 11 5 3 3 2 2 2
Количество двоек: 3
Квадраты элементов: 4 4 4 9 9 25 121
Вывод через for_each: 11 5 3 3 2 2 2

========== ЗАДАНИЕ 3.2: Работа с list ==========
Исходный список: 2 2 2 3 3 5 11
Список после удаления двоек: 3 3 5 11
Поиск элемента 5: найден
Количество элементов > 3: 2

========== ЗАДАНИЕ 3.3: Работа с map ==========
Содержимое map (множитель -> количество):
2 -> 3
3 -> 2
5 -> 1
11 -> 1
Поиск ключа 3: найден, количество = 2
Количество множителей, встречающихся более 1 раза: 2

========== ЗАДАНИЕ 3.4: Работа с несколькими контейнерами ==========
Вектор множителей: 2 2 2 3 3 5 11
Вектор квадратов: 4 4 4 9 9 25 121
Скалярное произведение: 2*4 + 2*4 + ... = 4+4+4+9+9+25+121 = ...// вычисляется программно
Вектор сумм: 6 6 6 12 12 30 132

Контрольные вопросы

  1. Какова сложность операций push и pop в вашей реализации стека?
  2. В чём разница между std::vector и std::list? Когда какой контейнер предпочтительнее?
  3. Что такое лямбда-выражение? Приведите синтаксис и пример использования.
  4. Что такое функтор? В чём его преимущество перед обычной функцией?
  5. Как работает алгоритм std::remove_if? Почему он не удаляет элементы напрямую?
  6. В чём особенность контейнера std::map? Как реализован поиск по ключу?
  7. Какие алгоритмы STL вы использовали? Для каких задач они применялись?
  8. В чём отличие алгоритмов STL от методов контейнеров?