Здравствуйте! Предлагаю обзор основных контейнеров C++ STL с пояснением их внутренней структуры, сложности операций и типичных сценариев применения.
Структура данных — формальный способ хранения и организации информации в памяти, определяющий расположение элементов, способы доступа к ним и эффективность операций. Выбор контейнера во многом влияет на производительность: при миллионных вызовах даже малейшая разница в скорости вставки или чтения может оказаться критичной.
STL содержит набор контейнеров для разных задач: от упорядоченных последовательностей до ассоциативных и хеш-структур. Давайте кратко рассмотрим их классификацию и особенности.
Классификация контейнеров STL
| Категория | Контейнеры | Описание |
|---|---|---|
| Последовательные | vector, deque, list, forward_list, array | Элементы упорядочены по позиции |
| Ассоциативные упорядоченные | set, multiset, map, multimap | Красно-чёрные деревья, автоматическая сортировка по ключу |
| Хеш-контейнеры | unordered_set, unordered_multiset, unordered_map, unordered_multimap | Хеш-таблицы, быстрый доступ по ключу |
| Адаптеры | stack, queue, priority_queue | Обёртки над другими контейнерами с ограниченным интерфейсом |
Последовательные контейнеры
std::vector
Динамический массив, хранящий элементы последовательно. Идеален для быстрого доступа по индексу и добавления в конец.
std::vector v = {1, 2, 3};
v.push_back(4);
v.emplace_back(5);
В основе — три указателя: begin, end заполненной части и end выделенного буфера. При исчерпании ёмкости происходит реаллокация с расширением в ≈1.5–2× и перемещением элементов.
| Операция | Аморт. | Худший случай |
|---|---|---|
| push_back() | O(1) | O(n) при копировании |
| insert/erase в середине | O(n) | O(n) |
| operator[] | O(1) | O(1) |
| pop_front() | O(n) | O(n) |
- Итераторы и указатели инвалидируются при реаллокации.
- Гарантирует непрерывную область памяти, отличная кэш-локальность.
- Лучше всего подходит для частых вставок в конец и редких операций внутри.
std::deque
Двусторонняя очередь, устроенная как массив мелких блоков под управлением таблицы указателей. Обеспечивает O(1) вставки/удаления на обоих концах.
std::deque d = {1, 2, 3};
d.push_front(0);
d.push_back(4);
| Операция | Средняя | Худший |
|---|---|---|
| push_front/back() | O(1) | O(1) |
| insert() в середине | O(n) | O(n) |
| operator[] | O(1) | O(1) |
- Блоки могут храниться негомогенно, ниже кэш-локальность, чем у vector.
- Итераторы в большинстве случаев остаются валидными.
- Используется внутри queue и stack по умолчанию.
std::list
Двусвязный список, где каждый узел хранит ссылки на соседей. Поддерживает O(1) вставку и удаление по итератору.
std::list l = {1, 2, 3};
auto it = l.begin();
std::advance(it, 1);
l.insert(it, 42);
| Операция | Сложность | Комментарий |
|---|---|---|
| insert/erase по итератору | O(1) | перенастройка ссылок |
| поиск по значению | O(n) | линейный обход |
| итерация | O(n) | низкая кэш-эффективность |
- Итераторы стабильны, кроме удаляемого элемента.
- Нет доступа по индексу, значительные накладные расходы памяти.
- Применяется при частых изменениях в середине и в алгоритмах LRU-кэша.
std::forward_list
Односвязный список с минимальным оверхедом: каждый элемент хранит указатель только на следующий узел.
std::forward_list fl = {1, 2, 3};
fl.push_front(0);
| Операция | Сложность |
|---|---|
| insert/erase после итератора | O(1) |
| поиск | O(n) |
| итерация | O(n) |
- Подходит для однопроходных алгоритмов и встраиваемых систем.
- Не поддерживает двунаправленный обход и размер за O(1).
std::array
Статический массив фиксированного размера, обёртка над C-массивом с интерфейсом STL.
std::array a = {1, 2, 3};
a[1] = 42;
| Операция | Сложность | Комментарий |
|---|---|---|
| operator[]/at() | O(1) | мгновенно |
| итерация | O(n) | линейно |
| изменение размера | — | фиксировано при компиляции |
- Совместим с алгоритмами STL,
at()проверяет границы. - Отличная кэш-локальность без динамики.
Ассоциативные упорядоченные контейнеры
Базируются на сбалансированных деревьях (обычно красно-чёрных), гарантируют отсортированный порядок и O(log n) для основных операций.
std::set / std::multiset
std::set хранит уникальные ключи, std::multiset допускает дубликаты.
std::set s = {3, 1, 2};
s.insert(4);
// s.insert(2) не добавит дубликат
std::multiset ms = {1, 2, 2, 3};
ms.insert(2);
ms.erase(2); // удалит только один экземпляр
| Операция | Сложность |
|---|---|
| insert/erase/find | O(log n) |
| итерация | O(n) |
- Итераторы стабильны при вставках.
- Используется для рейтингов, топ-N, поиска по диапазону.
std::map / std::multimap
std::map — ассоциативный массив ключ→значение с уникальными ключами, std::multimap — допускает множественные значения на один ключ.
std::map m = {{"Alice",25}, {"Bob",30}};
m["Charlie"] = 28;
std::multimap mm;
mm.insert({"Alice",90});
mm.insert({"Alice",95});
auto range = mm.equal_range("Alice");
for (auto it = range.first; it != range.second; ++it)
std::cout << it->first << ": " << it->second << "\n";
| Операция | Сложность |
|---|---|
| insert/erase/find | O(log n) |
| итерация | O(n) |
operator[]создаёт элемент по ключу, если не существует.- Можно задавать свой компаратор.
- Подходит для словарей, конфигураций, иерархических данных.
Хеш-контейнеры
Реализованы через хеш-таблицы, средняя сложность операций O(1), в худшем при коллизиях O(n).
std::unordered_set / std::unordered_multiset
unordered_set хранит уникальные элементы, unordered_multiset — допускает дубликаты.
std::unordered_set us = {"Alice","Bob"};
us.insert("Charlie");
if (us.contains("Bob")) std::cout<<"Bob есть\n";
std::unordered_multiset ums;
ums.insert(10); ums.insert(10);
std::cout<<"Число 10: "<
| Операция | Средняя | Худший |
|---|---|---|
| insert/erase/find | O(1) | O(n) |
| итерация | O(n) | O(n) |
- Итераторы инвалидируются при rehash().
- Используется для кэшей, фильтров, быстрых проверок наличия.
std::unordered_map / std::unordered_multimap
unordered_map — ассоциативный массив с хешем, unordered_multimap — несколько значений на ключ.
std::unordered_map um = {{"Alice",25},{"Bob",30}};
um["Charlie"] = 28;
for (auto& [k,v]:um) std::cout<
| Операция | Средняя | Худший |
|---|---|---|
| insert/erase/find | O(1) | O(n) |
| итерация | O(n) | O(n) |
- Поддерживает кастомные хеш-функции и сравнения.
- Используется в парсерах, кэшах, таблицах маршрутов.
Адаптеры контейнеров
Обёртки над другими контейнерами с урезанным интерфейсом и специализированным поведением.
std::stack
Стек LIFO. По умолчанию базируется на deque, но может использовать vector или list.
std::stack st;
st.push(10); st.push(20);
std::cout<
- Операции push/pop/top за O(1).
- Нет итераторов, доступ только к верхнему элементу.
- Применяется в DFS, парсерах, undo/redo, вызовах функций.
std::queue
Очередь FIFO на базе deque по умолчанию.
std::queue q;
q.push(10); q.push(20);
std::cout<
- push/front/back/pop за O(1).
- Используется для планирования задач, сетевых буферов.
std::priority_queue
Очередь с приоритетом (макс-куча) на базе vector. Для min-heap используйте std::greater.
std::priority_queue pq;
pq.push(5); pq.push(10);
std::cout<, std::greater> minq;
minq.push(50); minq.push(10);
while(!minq.empty()){
std::cout<
- push/pop — O(log n), top — O(1).
- Применяется в алгоритмах Дейкстры, планировщиках, таймерах.
Рекомендации по выбору контейнера
| Задача | Контейнер | Причина |
|---|---|---|
| Быстрый доступ по индексу | std::vector | непрерывная память, O(1) |
| Частые вставки в середину | std::list | O(1), стабильные итераторы |
| Двусторонняя очередь | std::deque | эффективно оба конца |
| Быстрый поиск по ключу | std::unordered_map | O(1) в среднем |
| Отсортированные данные | std::set / std::map | гарантированный порядок |
| Приоритетная обработка | std::priority_queue | max-heap |
| Минимальные накладные расходы | std::forward_list | 1 указатель на элемент |
| Фиксированные небольшие массивы | std::array | нет динамики |



