Какие контейнеры STL ты знаешь?

Ответ

В C++ STL предоставляет несколько категорий контейнеров:

1. Последовательные контейнеры:

  • std::vector — динамический массив с произвольным доступом. Основной выбор для большинства случаев.
  • std::deque — двусторонняя очередь, позволяет эффективно добавлять/удалять элементы с обоих концов.
  • std::list — двусвязный список. Эффективные вставка и удаление в любой позиции, но нет произвольного доступа.
  • std::forward_list (C++11) — односвязный список с меньшим overhead.
  • std::array (C++11) — статический массив фиксированного размера, обёрнутый в STL-интерфейс.

2. Ассоциативные контейнеры (упорядоченные):

  • std::set / std::multiset — хранят ключи (значения) в отсортированном порядке, обычно на основе красно-чёрного дерева.
  • std::map / std::multimap — хранят пары ключ-значение, отсортированные по ключу.

3. Ассоциативные контейнеры (неупорядоченные, хэш-таблицы, C++11):

  • std::unordered_set / std::unordered_multiset — реализация на основе хэш-таблицы, обеспечивают амортизированное O(1) для поиска.
  • std::unordered_map / std::unordered_multimap — хэш-таблица для пар ключ-значение.

4. Адаптеры контейнеров:

  • std::stack — LIFO (последним пришёл — первым ушёл).
  • std::queue — FIFO (первым пришёл — первым ушёл).
  • std::priority_queue — очередь, где элемент с наивысшим приоритетом извлекается первым.

Пример использования std::vector и std::unordered_map:

#include <vector>
#include <unordered_map>
#include <string>

int main() {
    // Последовательный контейнер
    std::vector<int> numbers = {10, 20, 30};
    numbers.push_back(40); // Добавление в конец

    // Ассоциативный неупорядоченный контейнер
    std::unordered_map<std::string, int> phoneBook;
    phoneBook["Alice"] = 12345;
    phoneBook["Bob"] = 67890;

    // Быстрый поиск по ключу
    auto it = phoneBook.find("Alice");
    if (it != phoneBook.end()) {
        // Найдено: it->first = "Alice", it->second = 12345
    }
    return 0;
}

Выбор контейнера зависит от требуемых операций (частая вставка/удаление, поиск, обход) и их сложности.

Ответ 18+ 🔞

А, ну это ж классика, ёпта! Сидишь такой, пишешь код, и тут бац — нужно выбрать, куда данные пихать. А в STL этого добра, контейнеров, овердохуища, прям глаза разбегаются. Сейчас разложу по полочкам, чтоб ты не тыкался, как слепой кот в сраку.

1. Последовательные, они же как в очереди стоят:

  • std::vector — это типа наш золотой универсал, динамический массив. В 95% случаев бери его и не парься. Быстрый доступ, в конец добавлять — огонь. Но в середину воткнуть — может быть больно, потому что всё после него придётся сдвигать.
  • std::deque — хитрая жопа, двусторонняя очередь. Хочешь спереди пихать, хочешь сзади — всё быстро. Но внутри он устроен хитро, не как простой массив.
  • std::list — это тебе двусвязный список, классика жанра. Нужно постоянно в середину лазить, вставлять-удалять? Вот твой выбор. Но найти в нём что-то по индексу — это пиздец, придётся идти от начала, как будто ты слепой.
  • std::forward_list — его младший брат-полупидор, односвязный. Чуть меньше жрёт памяти, но и ходить по нему можно только вперёд.
  • std::array — ну это просто старый добрый массив, но приодетый в смокинг STL. Размер фиксированный при компиляции, никаких сюрпризов.

2. Ассоциативные, упорядоченные (всё по полочкам):

  • std::set / multiset — хранят просто значения (ключи), и всегда держат их отсортированными. Внутри обычно красно-чёрное дерево. multiset — для тех, кто любит повторения.
  • std::map / multimap — а вот это уже парочки ключ -> значение. Опять же, ключи отсортированы. Нужен телефонный справочник по фамилиям в алфавитном порядке? Сюда.

3. Ассоциативные, неупорядоченные (они же быстрые, на хешах):

  • std::unordered_set / unordered_multiset — вот это реально часто нужно! Хеш-таблица. Засунул значение — нашёл его за O(1) в среднем. Порядка нет, зато скорость, ёб твою мать!
  • std::unordered_map / unordered_multimap — король современных C++ проектов. Та же хеш-таблица для пар ключ-значение. Нужно быстро найти что-то по строке, например? Да без проблем! phoneBook["Alice"] — и всё, ты уже на месте.

4. Адаптеры (специальные штуки для своих дел):

  • std::stack — LIFO. Сложил тарелки — сверху берёшь. Глупый, но нужный.
  • std::queue — FIFO. Как очередь в магазине, кто первый пришёл, тот первый и обслужен.
  • std::priority_queue — очередь с приоритетом. Типа в больнице: не важно кто пришёл, а у кого нога оторвана — того первым тащат.

Смотри, как на практике выглядит, на простом примере:

#include <vector>
#include <unordered_map>
#include <string>

int main() {
    // Вектор — наше всё для кучи чисел
    std::vector<int> numbers = {10, 20, 30};
    numbers.push_back(40); // Добавили в конец — быстро и просто

    // А вот unordered_map — вещь! Быстрый поиск по имени.
    std::unordered_map<std::string, int> phoneBook;
    phoneBook["Alice"] = 12345; // Записали
    phoneBook["Bob"] = 67890;

    // Ищем Алису. Очень быстро, потому что хеш.
    auto it = phoneBook.find("Alice");
    if (it != phoneBook.end()) {
        // Всё, нашли! it->first — это "Alice", it->second — её номер 12345.
    }
    return 0;
}

Короче, чувак, выбор зависит от того, что ты чаще делаешь: постоянно ищешь по ключу — бери unordered_map. Просто копишь кучу данных и бегаешь по ним — vector. Нужно порядок соблюдать — set или map. Главное — понимать, что внутри происходит, а не тыкать наугад, а потом удивляться, почему всё тормозит, как хуй в пальто.