<MyRusakov.ru />

Профессия Web-разработчик

Профессия Web-разработчик

Этот комплект за 8 месяцев превратит Вас в Web-разработчика с нуля. Учиться достаточно 1 час в день.

Начнёте Вы с HTML, CSS и вёрстки сайтов. Потом перейдёте к программированию и JavaScript. Затем изучите PHP, MySQL, SQL, Python. Изучите Web-фреймворки Laravel и Django. Создадите 5 своих сайтов для портфолио.

Комплект содержит:

- 540 видеоуроков

- 110 часов видео

- 1205 заданий для закрепления материала из уроков

- 5 финальных тестов

- 7 сертификатов

- 12 Бонусных курсов

Подробнее
Подписка

Подпишитесь на мой канал на YouTube, где я регулярно публикую новые видео.

YouTube Подписаться

Подписавшись по E-mail, Вы будете получать уведомления о новых статьях.

Подписка Подписаться

Добавляйтесь ко мне в друзья ВКонтакте! Отзывы о сайте и обо мне оставляйте в моей группе.

Мой аккаунт Мой аккаунт Моя группа
Опрос

Какая тема Вас интересует больше?

std::set в C++: уникальные элементы, поиск и сортировка на практике

std::set в C++: уникальные элементы, поиск и сортировка на практике

std::set в C++ — это ассоциативный контейнер из стандартной библиотеки STL. Он полезен, когда нужно хранить набор уникальных значений, быстро проверять наличие элемента и получать данные в отсортированном виде без ручной сортировки.

Типичный поисковый запрос по этой теме — «std::set C++ примеры» или «как хранить уникальные элементы в C++». Именно под такую практическую задачу и разберём контейнер set.

Что такое std::set

std::set находится в заголовочном файле <set>. Внутри он обычно реализован как сбалансированное бинарное дерево, поэтому основные операции работают за O(log n):

  • добавление элемента;
  • поиск элемента;
  • удаление элемента.

Главные свойства std::set:

  • элементы не повторяются;
  • значения автоматически сортируются;
  • нельзя изменить элемент прямо внутри set так, чтобы нарушился порядок;
  • итерация идёт по возрастанию, если не задан другой компаратор.

Простой пример std::set

Допустим, пользователь вводит числа, а нам нужно вывести только уникальные значения по возрастанию.

#include <iostream>
#include <set>

int main() {
    std::set<int> numbers;

    numbers.insert(5);
    numbers.insert(2);
    numbers.insert(10);
    numbers.insert(2); // повтор, не будет добавлен

    for (int value : numbers) {
        std::cout << value << ' ';
    }

    return 0;
}

Результат:

2 5 10

Обратите внимание: мы добавили 2 два раза, но в контейнере он остался только один раз. Кроме того, значения вывелись не в порядке добавления, а в отсортированном порядке.

Как понять, был ли элемент добавлен

Метод insert возвращает пару: итератор и логическое значение. Второе значение равно true, если элемент действительно добавлен, и false, если такой элемент уже был.

#include <iostream>
#include <set>

int main() {
    std::set<std::string> names;

    auto result = names.insert("Anna");

    if (result.second) {
        std::cout << "Имя добавлено\n";
    } else {
        std::cout << "Такое имя уже есть\n";
    }

    result = names.insert("Anna");

    if (!result.second) {
        std::cout << "Повторное добавление не сработало\n";
    }

    return 0;
}

Это удобно, например, при регистрации логинов, тегов, идентификаторов или уникальных кодов.

Поиск элемента в std::set

Для поиска используется метод find. Если элемент найден, возвращается итератор на него. Если нет — end().

#include <iostream>
#include <set>

int main() {
    std::set<int> ids = {101, 205, 310, 404};

    int target = 205;

    if (ids.find(target) != ids.end()) {
        std::cout << "ID найден\n";
    } else {
        std::cout << "ID не найден\n";
    }

    return 0;
}

В C++20 можно использовать более читаемый метод contains:

if (ids.contains(205)) {
    std::cout << "Есть такой ID\n";
}

Если вы пишете код под C++17 или более старый стандарт, используйте find.

Удаление элементов

Удалять элементы можно по значению или по итератору. Самый простой вариант — erase(value).

#include <iostream>
#include <set>

int main() {
    std::set<int> numbers = {1, 2, 3, 4, 5};

    numbers.erase(3);

    for (int number : numbers) {
        std::cout << number << ' ';
    }

    return 0;
}

Результат:

1 2 4 5

Метод erase возвращает количество удалённых элементов. Для std::set это будет либо 0, либо 1, потому что дубликатов нет.

Сортировка по убыванию

По умолчанию std::set сортирует значения по возрастанию. Чтобы хранить элементы по убыванию, можно передать компаратор std::greater.

#include <iostream>
#include <set>
#include <functional>

int main() {
    std::set<int, std::greater<int>> numbers = {4, 1, 7, 2};

    for (int number : numbers) {
        std::cout << number << ' ';
    }

    return 0;
}

Результат:

7 4 2 1

std::set со своими структурами

Чтобы хранить в set объекты пользовательского типа, нужно объяснить контейнеру, как их сравнивать. Например, отсортируем пользователей по возрасту, а при равном возрасте — по имени.

#include <iostream>
#include <set>
#include <string>

struct User {
    std::string name;
    int age;
};

struct UserCompare {
    bool operator()(const User& a, const User& b) const {
        if (a.age != b.age) {
            return a.age < b.age;
        }

        return a.name < b.name;
    }
};

int main() {
    std::set<User, UserCompare> users;

    users.insert({"Ivan", 25});
    users.insert({"Anna", 20});
    users.insert({"Petr", 25});

    for (const User& user : users) {
        std::cout << user.name << ": " << user.age << '\n';
    }

    return 0;
}

Важно: для std::set два элемента считаются одинаковыми не через ==, а через компаратор. Если ни один объект не меньше другого, set считает их эквивалентными.

Когда использовать std::set

std::set хорошо подходит, если вам нужно:

  • хранить только уникальные элементы;
  • часто проверять наличие значения;
  • получать элементы сразу в отсортированном порядке;
  • быстро находить ближайшие элементы через lower_bound и upper_bound.

Пример с lower_bound: найдём первое число, которое не меньше заданного.

std::set<int> numbers = {10, 20, 30, 40};

auto it = numbers.lower_bound(25);

if (it != numbers.end()) {
    std::cout << "Первое подходящее число: " << *it << '\n';
}

Здесь будет найдено число 30.

std::set, std::unordered_set или vector?

Новички часто выбирают std::set «на всякий случай», но это не всегда лучший вариант.

  • std::set — элементы уникальны и отсортированы, операции обычно O(log n).
  • std::unordered_set — элементы уникальны, но порядок не гарантируется, поиск в среднем O(1).
  • std::vector — хорош, если данных мало или важен порядок добавления; для уникальности придётся писать дополнительную проверку.

Если сортировка не нужна, а важна максимальная скорость поиска, часто лучше подойдёт std::unordered_set. Если элементов немного, простой vector может оказаться понятнее и быстрее из-за меньших накладных расходов.

Частые ошибки новичков

  • Ожидать порядок добавления. std::set хранит элементы отсортированно, а не в порядке вставки.
  • Пытаться изменить элемент через итератор. Это запрещено, потому что изменение может сломать внутренний порядок дерева.
  • Забывать про компаратор. Для своих типов нужно явно определить логику сравнения.
  • Использовать set там, где нужен multiset. Если дубликаты должны сохраняться, берите std::multiset.

Практический пример: уникальные слова из текста

Ниже программа считывает слова до конца ввода и выводит уникальные слова в алфавитном порядке.

#include <iostream>
#include <set>
#include <string>

int main() {
    std::set<std::string> words;
    std::string word;

    while (std::cin >> word) {
        words.insert(word);
    }

    std::cout << "Уникальные слова:\n";

    for (const std::string& item : words) {
        std::cout << item << '\n';
    }

    return 0;
}

Такой код можно использовать как основу для простого анализа текста, списка тегов или проверки повторяющихся значений.

Рекомендации

  • Используйте std::set, когда нужны уникальность и сортировка одновременно.
  • Для проверки наличия в C++20 предпочитайте contains, а в C++17 — find.
  • Не храните в set изменяемые данные, по которым выполняется сравнение.
  • Для пользовательских структур всегда тщательно продумывайте компаратор.

Если вы хотите уверенно разобраться не только с std::set, но и со всей базой C++: типами, функциями, ООП, STL и практическими задачами, посмотрите курс «Программирование на C++ с Нуля до Гуру» — пошаговый путь от первых программ до уверенной разработки.

Вывод

std::set в C++ — удобный контейнер для уникальных отсортированных данных. Он не заменяет все остальные контейнеры, но отлично решает задачи, где нужно быстро проверять наличие элемента и поддерживать порядок. Главное — помнить, что set не хранит порядок вставки и определяет уникальность через правило сравнения.

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (https://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: https://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: https://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как создать свой сайт

  2. Текстовая ссылка:

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Комментарии (0):

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.