⚙️ C++

Множества (Set) в C++17: подробное руководство

Что такое Множества (Set)?

Представь себе коробку для хранения уникальных игрушек. Если ты попытаешься положить туда два одинаковых мячика, то второй мячик просто не поместится — в коробке уже есть такой же. В C++ такой «коробкой» для данных является std::set. Это коллекция, которая хранит элементы только в единственном экземпляре и автоматически сортирует их по возрастанию.

Зачем это нужно? Например, чтобы:

  • Убрать дубликаты из списка (оставить только уникальные товары).
  • Проверить, есть ли элемент в коллекции (записан ли контакт в книжку).
  • Объединить два списка без повторов (список покупок от двух друзей).

Как подключить и объявить set?

Чтобы использовать set, нужно подключить библиотеку #include <set>.

#include <iostream>
#include <set> // Подключаем множества
#include <string>

int main() {
    // Создаем множество строк
    std::set<std::string> ingredients;
    
    // Создаем множество целых чисел
    std::set<int> numbers;
    
    return 0;
}

Как добавить элемент?

Используем метод .insert(). Если элемент уже есть, он не добавится повторно.

#include <iostream>
#include <set>

int main() {
    std::set<int> mySet;
    
    mySet.insert(10);
    mySet.insert(5);
    mySet.insert(10); // Этот элемент не добавится, так как 10 уже есть!
    mySet.insert(3);
    
    // Выведем все элементы (они будут отсортированы)
    for(int num : mySet) {
        std::cout << num << " ";
    }
    // Вывод: 3 5 10
    return 0;
}

Как проверить, есть ли элемент?

Метод .count() возвращает 1 (если элемент есть) или 0 (если нет).

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

int main() {
    std::set<std::string> contacts = {"Аня", "Борис", "Вика"};
    
    std::string name;
    std::cout << "Введите имя для поиска: ";
    std::cin >> name;
    
    if(contacts.count(name) == 1) {
        std::cout << "Контакт найден!";
    } else {
        std::cout << "Контакта нет в книжке.";
    }
    return 0;
}

Как удалить элемент?

Метод .erase() удаляет элемент по значению.

#include <iostream>
#include <set>

int main() {
    std::set<std::string> shopping = {"молоко", "хлеб", "яйца", "сыр"};
    
    std::string item;
    std::cout << "Что хотите убрать из списка? ";
    std::cin >> item;
    
    shopping.erase(item); // Удаляем элемент
    
    std::cout << "Осталось в списке: ";
    for(const auto& thing : shopping) {
        std::cout << thing << " ";
    }
    return 0;
}

Как узнать размер множества?

Метод .size() возвращает количество элементов.

#include <iostream>
#include <set>

int main() {
    std::set<int> numbers = {1, 2, 3, 4, 5, 5, 5}; // Повторы не считаются
    std::cout << "Размер множества: " << numbers.size(); // Вывод: 5
    return 0;
}

Разбор типичных задач

Задача 1. Уникальные ингредиенты пиццы

Пользователь вводит ингредиенты через запятую. Нужно вывести только уникальные.

#include <iostream>
#include <set>
#include <sstream> // Для работы со строками
#include <string>

int main() {
    std::string input;
    std::cout << "Введите ингредиенты через запятую: ";
    std::getline(std::cin, input);
    
    std::set<std::string> uniqueIngredients;
    std::stringstream ss(input);
    std::string ingredient;
    
    // Разделяем строку по запятым
    while(std::getline(ss, ingredient, ',')) {
        // Убираем лишние пробелы в начале и конце
        if(!ingredient.empty() && ingredient[0] == ' ') {
            ingredient = ingredient.substr(1);
        }
        if(!ingredient.empty()) {
            uniqueIngredients.insert(ingredient);
        }
    }
    
    std::cout << "Уникальные ингредиенты: ";
    for(const auto& item : uniqueIngredients) {
        std::cout << item << " ";
    }
    return 0;
}

Задача 2. Объединение списков покупок

Есть два списка покупок. Нужно их объединить без повторов.

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

int main() {
    std::set<std::string> mainList = {"кола", "чипсы", "пицца"};
    std::set<std::string> guestList = {"сок", "чипсы", "орешки"};
    
    // Добавляем элементы из второго списка в первый
    for(const auto& item : guestList) {
        mainList.insert(item);
    }
    
    std::cout << "Финальный список покупок: ";
    for(const auto& product : mainList) {
        std::cout << product << " ";
    }
    // Вывод: кола орешки пицца сок чипсы (все уникально и отсортировано)
    return 0;
}

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

  • Путают set с vector. В set нельзя обратиться по индексу (mySet[0] — ошибка). Используйте цикл for или итераторы.
  • Забывают про #include <set>. Без этой строки компилятор не поймет, что такое std::set.
  • Думают, что insert добавит элемент, даже если он есть. Нет, insert не создает дубликатов. Если нужно хранить повторы — используйте std::multiset.
  • Пытаются изменить элемент в set. Элементы в set — константные. Нельзя написать *it = 5;. Сначала удалите старый, потом добавьте новый.

Итоговый пример: Записная книжка контактов

Создадим программу, которая хранит имена контактов (только уникальные) и позволяет их искать.

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

int main() {
    std::set<std::string> phoneBook;
    std::string command;
    
    std::cout << "Записная книжка контактов\n";
    std::cout << "Команды: add Имя, find Имя, list, exit\n";
    
    while(true) {
        std::cout << "> ";
        std::cin >> command;
        
        if(command == "add") {
            std::string name;
            std::cin >> name;
            phoneBook.insert(name);
            std::cout << "Контакт \"" << name << "\" добавлен.\n";
        }
        else if(command == "find") {
            std::string name;
            std::cin >> name;
            if(phoneBook.count(name) == 1) {
                std::cout << "Контакт \"" << name << "\" найден.\n";
            } else {
                std::cout << "Контакт не найден.\n";
            }
        }
        else if(command == "list") {
            std::cout << "Все контакты:\n";
            for(const auto& contact : phoneBook) {
                std::cout << "- " << contact << "\n";
            }
        }
        else if(command == "exit") {
            break;
        }
        else {
            std::cout << "Неизвестная команда.\n";
        }
    }
    return 0;
}

Вывод программы:
Записная книжка контактов
Команды: add Имя, find Имя, list, exit
> add Маша
Контакт "Маша" добавлен.
> add Петя
Контакт "Петя" добавлен.
> add Маша
Контакт "Маша" добавлен. (Но в set он уже был, поэтому ничего не изменилось)
> list
Все контакты:
- Маша
- Петя
> exit

Множества (set) — это мощный инструмент для работы с уникальными данными. Они автоматически сортируют элементы и не допускают дубликатов. Используй их, когда нужно быстро проверить наличие элемента или избавиться от повторений!