好的!既然你提到了 C++ 标准库中的容器类 <set>,我将为你详细讲解它的用法,包括基本概念、特性、功能以及实用示例。<set> 是 C++ STL 中的一种关联容器,基于平衡二叉搜索树(通常是红黑树)实现,元素按升序自动排序且唯一。以下是完整的内容,适合学习和实践。


C++ 标准库:<set>

<set> 是一个模板类,表示有序集合。它的主要特点是:

  • 自动排序:元素按升序存储,默认使用 < 比较。
  • 唯一性:不允许重复元素。
  • 高效查找:基于平衡二叉树,查找、插入、删除的时间复杂度为 O(log n)。
  • 动态大小:大小随元素增减动态调整。

包含头文件

#include <set>
#include <iostream> // 通常配合使用

定义格式

std::set<类型, 比较器> 变量名;
  • 类型:集合中元素的数据类型(如 intdoublestd::string)。
  • 比较器:可选,默认是 std::less<T>(升序),可改为 std::greater<T>(降序)。

基本用法

1. 声明和初始化

#include <set>
#include <iostream>

int main() {
    std::set<int> s;              // 空集合
    std::set<int> s2 = {1, 2, 3, 4, 2}; // 初始化列表(C++11 起),重复的 2 被忽略

    std::cout << "s2: ";
    for (int val : s2) {
        std::cout << val << " "; // 输出:1 2 3 4
    }
    std::cout << std::endl;
    return 0;
}
  • 重复元素自动被忽略。

2. 插入元素

#include <set>
#include <iostream>

int main() {
    std::set<int> s;
    s.insert(10); // 插入
    s.insert(30);
    s.insert(20);
    s.insert(10); // 重复插入无效

    std::cout << "s: ";
    for (int val : s) {
        std::cout << val << " "; // 输出:10 20 30
    }
    std::cout << std::endl;
    return 0;
}
  • insert():O(log n),返回 std::pair<iterator, bool>(迭代器和是否插入成功)。

3. 删除元素

#include <set>
#include <iostream>

int main() {
    std::set<int> s = {1, 2, 3, 4};
    s.erase(2); // 删除值为 2 的元素

    std::cout << "After erase: ";
    for (int val : s) {
        std::cout << val << " "; // 输出:1 3 4
    }
    std::cout << std::endl;
    return 0;
}
  • erase():O(log n),可按值或迭代器删除。

4. 查找元素

#include <set>
#include <iostream>

int main() {
    std::set<int> s = {10, 20, 30};
    auto it = s.find(20); // 查找值为 20 的元素
    if (it != s.end()) {
        std::cout << "Found: " << *it << std::endl; // 输出:Found: 20
    } else {
        std::cout << "Not found" << std::endl;
    }

    if (s.count(10)) { // 检查元素是否存在
        std::cout << "10 exists" << std::endl; // 输出:10 exists
    }
    return 0;
}
  • find():O(log n),返回迭代器,找不到返回 end()
  • count():O(log n),返回元素个数(0 或 1,因 <set> 无重复)。

5. 大小和状态

#include <set>
#include <iostream>

int main() {
    std::set<int> s = {1, 2, 3};
    std::cout << "Size: " << s.size() << std::endl;         // 输出:3
    std::cout << "Empty? " << (s.empty() ? "Yes" : "No") << std::endl; // 输出:No
    return 0;
}
  • size():O(1)。
  • empty():O(1)。

6. 自定义比较器(降序)

#include <set>
#include <iostream>

int main() {
    std::set<int, std::greater<int>> s; // 降序集合
    s.insert(10);
    s.insert(30);
    s.insert(20);

    std::cout << "s: ";
    for (int val : s) {
        std::cout << val << " "; // 输出:30 20 10
    }
    std::cout << std::endl;
    return 0;
}

7. 迭代器

#include <set>
#include <iostream>

int main() {
    std::set<std::string> s = {"Apple", "Banana", "Cherry"};
    std::cout << "Forward: ";
    for (auto it = s.begin(); it != s.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl; // 输出:Apple Banana Cherry

    std::cout << "Reverse: ";
    for (auto it = s.rbegin(); it != s.rend(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl; // 输出:Cherry Banana Apple
    return 0;
}
  • 支持双向迭代器:begin()/end()rbegin()/rend()

与其他容器的对比

特性std::setstd::vectorstd::list
数据结构平衡二叉树动态数组双向链表
元素顺序自动排序插入顺序插入顺序
重复元素不允许允许允许
查找O(log n)O(n)O(n)
插入/删除O(log n)O(n)O(1)
随机访问不支持O(1)不支持

注意事项

  1. 唯一性:重复插入无效,需用 <multiset> 支持重复元素。
  2. 迭代器失效:插入不影响现有迭代器,删除使指向被删元素的迭代器失效。
  3. 性能:查找和插入高效,但不支持随机访问。
  4. 内存:红黑树结构有额外指针开销。

实用示例:统计唯一单词

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

int main() {
    std::set<std::string> words;
    std::string input;
    std::cout << "Enter words (Ctrl+D or Ctrl+Z to stop):\n";
    while (std::cin >> input) {
        words.insert(input);
    }

    std::cout << "Unique words:\n";
    for (const std::string& word : words) {
        std::cout << word << "\n";
    }
    std::cout << "Total unique words: " << words.size() << std::endl;
    return 0;
}

输入示例:

apple banana apple cherry banana
Ctrl+D

输出:

Unique words:
apple
banana
cherry
Total unique words: 3

小练习

以下是一个基于 <set> 的练习题,你可以尝试:

问题:

写一个 C++ 程序:

  1. 定义一个 std::set<int>
  2. 插入 10、20、30、20、40(注意重复)。
  3. 删除值为 20 的元素。
  4. 输出所有元素和大小。

参考答案(你可以先尝试)

#include <set>
#include <iostream>

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

    // 插入元素
    s.insert(10);
    s.insert(20);
    s.insert(30);
    s.insert(20); // 重复无效
    s.insert(40);

    // 删除 20
    s.erase(20);

    // 输出所有元素和大小
    std::cout << "Elements: ";
    for (int val : s) {
        std::cout << val << " ";
    }
    std::cout << "\nSize: " << s.size() << std::endl;

    return 0;
}

输出:

Elements: 10 30 40
Size: 3

下一步

  • 如果你想试试这个练习,请写下你的代码,我会帮你检查。
  • 如果你想要更复杂的内容(如自定义比较器、与 <multiset> 对比),或者设计 <set> 的测验题,请告诉我!
  • 你也可以提出具体问题,如“如何用 <set> 实现交集?”或“<set><unordered_set> 的区别?”。

现在,你想做什么?试试练习,还是有其他需求?