Основы программирования на C++: Адаптеры и представления
Прежде чем читать мою статью - реши для себя, зачем ты это делаешь. Даже если ты просто нормальный человек, лишним не будет.
Если вы настоящий профессионал программирования, то зачем вы тут сидите и читайте статью на пикабу? Если вы ради интереса зашли почитать, то претензий ноль, но если вы просто захотели задушить нового пользователя Пикабу минусами, то немедленно покиньте статью, вам однозначно интересно не будет.
Здравствуйте, мои маленькие любители программирования!
Адаптеры и представления — это инструменты, которые позволяют работать с данными более удобным и эффективным способом, не создавая новых копий данных. Они не являются самостоятельными контейнерами, а используют существующие структуры для хранения данных, предоставляя при этом специализированный интерфейс.
Адаптеры
1. std::stack
Стек — это структура данных, которая работает по принципу LIFO (Last In, First Out). Это означает, что последний добавленный элемент будет первым извлеченным.
Основные операции:
push: Добавляет элемент на вершину стека.
pop: Удаляет элемент с вершины стека.
top: Возвращает элемент на вершине стека без его удаления.
empty: Проверяет, пуст ли стек.
#include <iostream>
#include <stack>
int main() {
std::stack<int> s;
s.push(1);
s.push(13);
s.pop(); // Удаляем элемент с вершины (останется только 1)
std::cout << s.top() << "\n"; // Выводим элемент на вершине (1)
if (s.empty()) {
std::cout << "Stack is empty\n";
}
}
Особенности:
По умолчанию std::stack использует std::deque для хранения данных, но можно заменить его на другой контейнер, например, std::list.
2. std::queue
Очередь реализует принцип FIFO (First In, First Out). Элементы добавляются в конец очереди, а извлекаются из начала.
Основные операции:
push: Добавляет элемент в конец очереди.
pop: Удаляет элемент из начала очереди.
front: Возвращает первый элемент очереди.
back: Возвращает последний элемент очереди.
empty: Проверяет, пуста ли очередь.
#include <iostream>
#include <queue>
int main() {
std::queue<int> q;
q.push(1);
q.push(13);
std::cout << q.front() << "\n"; // Выводит первый элемент (1)
std::cout << q.back() << "\n"; // Выводит последний элемент (13)
q.pop(); // Удаляем первый элемент (останется только 13)
if (q.empty()) {
std::cout << "Queue is empty\n";
}
}
Особенности:
По умолчанию используется std::deque, но можно заменить его на другой контейнер, поддерживающий операции push_back, pop_front и т. д.
3. std::priority_queue
Очередь с приоритетами позволяет быстро получать максимальный (или минимальный) элемент. Она основана на структуре данных "куча".
Основные операции:
push: Добавляет элемент в очередь.
pop: Удаляет максимальный элемент.
top: Возвращает максимальный элемент без его удаления.
empty: Проверяет, пуста ли очередь.
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> pq;
for (int x : {3, 14, 15, 92, 6, 0, 1, 10}) {
pq.push(x);
}
while (!pq.empty()) {
std::cout << pq.top() << "\n"; // Выводит элементы в порядке убывания
pq.pop();
}
}
Особенности:
По умолчанию используется std::vector для хранения данных.
Можно изменить порядок сортировки, передав третий параметр, например, std::greater<int> для получения минимального элемента.
Представления
1. std::string_view
std::string_view — это легковесная обертка над строкой, которая позволяет работать с подстроками без создания их копий. Она не владеет памятью, а лишь ссылается на существующую строку.
Преимущества:
Быстрее, чем создание новой строки (std::string).
Экономит память, так как не копирует данные.
#include <iostream>
#include <string>
#include <string_view>
int main() {
std::string s = "Hello, world! How do you do?";
std::string_view sv = s; // Создаем string_view для строки s
auto sub = sv.substr(7, 5); // Выделяем подстроку "world"
std::cout << sub << "\n"; // Выводит "world"
std::cout << s << "\n"; // Исходная строка не изменилась
}
Ограничения:
std::string_view не позволяет изменять строку.
Ссылается на исходную строку, поэтому если строка уничтожается, string_view становится невалидным.
2. Пример некорректного использования std::string_view
Если исходная строка выходит из области видимости, string_view теряет доступ к данным:
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
int main() {
std::vector<std::string_view> lines;
for (int i = 0; i < 5; ++i) {
std::string line;
std::getline(std::cin, line);
lines.push_back(line); // string_view ссылается на временную строку
}
for (auto item : lines) {
std::cout << item << "\n"; // Ошибка! Строки уже уничтожены
}
}
Решение: Используйте std::string, если данные должны сохраняться после выхода из области видимости.
3. std::span
std::span — аналог std::string_view, но для массивов или векторов. Он предоставляет доступ к непрерывной последовательности элементов в памяти.
#include <iostream>
#include <vector>
#include <span>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
std::span<int> span(v); // Создаем span для вектора
for (int x : span) {
std::cout << x << " "; // Выводит элементы вектора
}
}
Особенности:
Не владеет данными.
Может использоваться для работы с подмассивами.
Заключение
Адаптеры и представления позволяют работать с данными более эффективно:
Адаптеры (std::stack, std::queue, std::priority_queue) предоставляют специализированный интерфейс для работы с контейнерами.
Представления (std::string_view, std::span) позволяют избежать ненужных копирований данных, но требуют осторожности при работе с временными объектами.
Задачи на std::stack
Обратный порядок слов
Напишите программу, которая принимает строку текста и выводит слова в обратном порядке, используя std::stack.
Пример:
Ввод: "Hello world from C++"
Вывод: "C++ from world Hello"Проверка правильности скобок
Напишите программу, которая проверяет, правильно ли расставлены скобки в строке (круглые, квадратные и фигурные). Используйте std::stack.
Пример:
Ввод: "{[()()]}" → Вывод: "Правильно"
Ввод: "{[(])}" → Вывод: "Неправильно"Калькулятор с обратной польской записью
Реализуйте калькулятор, который вычисляет выражения в обратной польской записи (RPN). Например:
Ввод: "3 4 + 2 * 7 /" → Вывод: 2
Задачи на std::queue
Симуляция очереди в банке
Напишите программу, которая моделирует очередь клиентов в банке. Каждый клиент имеет номер. Программа должна позволять добавлять новых клиентов в очередь и обслуживать их по принципу FIFO.
Пример:
Ввод:add 1
add 2
serve
Вывод: "Client 1 served"
Поиск минимального элемента в очереди
Напишите программу, которая находит минимальный элемент в очереди за один проход. Используйте только операции push, pop и front.Симуляция круговой очереди
Реализуйте круговую очередь (например, карусель), где после обслуживания последнего клиента очередь начинается заново. Программа должна позволять добавлять новых клиентов и обслуживать их.
Задачи на std::priority_queue
Топ-N самых больших чисел
Напишите программу, которая находит N самых больших чисел из потока данных. Используйте std::priority_queue.
Пример:
Ввод: N = 3, данные = {5, 1, 9, 2, 8, 3} → Вывод: {9, 8, 5}Сортировка задач по приоритету
У вас есть список задач с приоритетами. Напишите программу, которая обрабатывает задачи в порядке убывания приоритета.
Пример:
Ввод:Task1 Priority=3
Task2 Priority=1
Task3 Priority=2
Вывод:
Task1
Task3
Task2
Минимальная куча
Реализуйте минимальную кучу с помощью std::priority_queue и напишите программу, которая выводит элементы в порядке возрастания.
Задачи на std::string_view
Подсчет подстрок
Напишите программу, которая подсчитывает количество вхождений заданной подстроки в строку, используя std::string_view.
Пример:
Ввод: "Hello world", "o" → Вывод: 2Разбиение строки на слова
Напишите программу, которая разбивает строку на слова, используя std::string_view.
Пример:
Ввод: "Hello world from C++" → Вывод:Hello
world
from
C++
Проверка префикса и суффикса
Напишите программу, которая проверяет, начинается ли строка с заданного префикса и заканчивается ли заданным суффиксом, используя std::string_view.
Пример:
Ввод: "Hello world", prefix="Hello", suffix="world" → Вывод: "Да"
Задачи на std::span
Сумма элементов массива
Напишите программу, которая вычисляет сумму элементов массива или вектора, используя std::span.
Пример:
Ввод: {1, 2, 3, 4, 5} → Вывод: 15Поиск максимального элемента
Напишите программу, которая находит максимальный элемент в массиве или векторе, используя std::span.Реверс части массива
Напишите программу, которая переворачивает часть массива, используя std::span.
Пример:
Ввод: {1, 2, 3, 4, 5}, start=1, end=4 → Вывод: {1, 4, 3, 2, 5}
Комбинированные задачи
Очередь с приоритетом задач
Реализуйте систему управления задачами, где каждая задача имеет приоритет и описание. Используйте std::priority_queue для хранения задач и std::string_view для работы с описанием.Стек с поддержкой минимума
Реализуйте стек, который поддерживает операцию получения минимального элемента за O(1). Используйте два std::stack.Анализ логов
Напишите программу, которая анализирует лог-файл и выводит топ-N самых частых сообщений. Используйте std::unordered_map для подсчета частот и std::priority_queue для выбора топ-N.Работа с подстроками
Напишите программу, которая находит самую длинную подстроку без повторяющихся символов, используя std::string_view.Многомерные массивы
Реализуйте функцию, которая работает с двумерным массивом через std::span. Например, найдите сумму всех элементов или максимальный элемент.