C++树形关联式容器set、multiset、map和multimap的使用

本文详细介绍了C++标准模板库(STL)中的四种树形关联容器:set、multiset、map和multimap,包括它们的存储内容、键值关系、排序方式、操作特性和实际使用示例,以及高级特性和应用场景。
摘要由CSDN通过智能技术生成

树形关联式容器的四种类型

在C++标准模板库(STL)中,树形关联式容器主要包括 set、multiset、map 和 multimap 四种类型。这四种容器均基于红黑树(RB-tree)实现,确保了元素的有序性,并且提供了高效的查找、插入和删除操作。以下是每种容器的详细介绍和使用说明:

1. std::set

  • 存储内容:set容器存储唯一键值,即每个元素都是唯一的,不能有重复。
  • 键值关系:在set中,元素本身即被视为键(key),没有对应的值(value)部分。
  • 排序:元素按照键的自然顺序或者通过自定义比较函数进行排序。
  • 操作:支持多种操作,如查找(findlower_boundupper_boundequal_range)、插入(insert)、删除(erase)、遍历等。
  • 示例
    std::set<int> s;
    s.insert(3);
    s.insert(1);
    s.insert(2);
    // set现在包含 [1, 2, 3]
    

2. std::multiset

  • 存储内容:multiset类似于set,但它允许键值重复。
  • 键值关系:与set一样,元素本身也是键,同样没有显式的值部分。
  • 排序:同样按照键的自然顺序或者自定义比较函数排序。
  • 操作:除了支持set的所有操作外,还允许插入多个具有相同键的元素。
  • 示例
    std::multiset<int> ms;
    ms.insert(3);
    ms.insert(1);
    ms.insert(2);
    ms.insert(2); // 可以插入重复元素
    // multiset现在包含 [1, 2, 2, 3]
    

3. std::map

  • 存储内容:map容器存储键值对(key-value pairs),键是唯一的,值可以重复。
  • 键值关系:每个元素都是一个std::pair<const Key, T>类型的对象,其中Key是键,T是值类型。
  • 排序:元素按照键的升序排列。
  • 操作:可以通过键访问对应的值,如查找(find)、插入(insert)、修改值(operator[])、删除(erase)等。
  • 示例
    std::map<std::string, int> m;
    m["apple"] = 1;
    m["banana"] = 2;
    // map现在包含 {"apple": 1, "banana": 2}
    

4. std::multimap

  • 存储内容:multimap类似于map,但允许键值对中有重复的键。
  • 键值关系:每个元素同样是键值对形式,但同一个键可以对应多个不同的值。
  • 排序:键依然有序,对于同一个键,其对应的多个值的顺序不确定。
  • 操作:支持map的所有操作,并增加了针对相同键值对的插入和遍历功能。
  • 示例
    std::multimap<std::string, int> mm;
    mm.insert({"apple", 1});
    mm.insert({"banana", 2});
    mm.insert({"apple", 3}); // 同一键可对应多个值
    // multimap现在包含 {"apple": [1, 3], "banana": [2]}
    

以上四种容器都支持迭代器遍历,常用于需要高效查找和有序存储场景,尤其是当元素的排序非常重要时。同时,由于底层实现为平衡二叉搜索树,因此平均时间复杂度接近O(log n)。

继续深入了解C++树形关联容器的特性与用法

共享特性

  • 迭代器稳定性:在set、multiset、map和multimap中,除了erase操作中的迭代器以及可能导致容器重新调整大小的操作(如insert时可能触发容器扩容)外,其他操作不会使已存在的迭代器失效。

  • 内存分配:这些容器会动态地管理内存,自动调整容量以适应元素数量的增长。

  • 效率:由于底层采用了红黑树,插入、删除和查找操作的时间复杂度通常是O(log n),n为容器中元素的数量。

  • 定制化:可以通过提供自定义的比较函数对象(Comparator)来改变元素的排序规则。此外,也可以通过指定分配器(Allocator)来自定义内存管理方式。

特殊用法及注意事项

  • 插入操作

    • insert方法不仅可以插入单个元素,还可以插入一个范围内的元素或一个由<initializer_list>初始化的元素序列。
    • 对于map和multimap,插入时如果不指定值,将默认构造一个值。
  • 查找操作

    • find方法返回一个指向所查找键的迭代器,如果未找到,则返回尾后迭代器。
    • lower_boundupper_bound分别返回第一个大于等于(包括等于)给定键的元素迭代器和第一个大于给定键的元素迭代器,这对于区间查询非常有用。
  • 删除操作

    • erase方法可以接受一个键、一个迭代器或一个迭代器范围来删除元素。对于map和multimap,删除的是键值对;而对于set和multiset,删除的就是元素本身。
  • 遍历

    • 除了普通的前向迭代遍历,还可以通过rbegin()rend()进行反向迭代遍历。

总结来说,C++中的set、multiset、map和multimap都是非常强大的容器,它们为程序员提供了方便的手段来管理和操作有序数据集合,尤其适合那些需要快速查找、插入和删除元素的场景。在实际编程过程中,应根据数据的特点(是否允许重复、关注键还是键值对)选择合适的容器类型。

高级特性和应用场景

  • 集合运算:set和multiset类容器支持集合间的数学运算,如并集(set_union)、交集(set_intersection)、差集(set_difference)和对称差集(set_symmetric_difference)。这些运算可以非常方便地用于处理集合型数据问题。

  • 键值对操作:对于map和multimap,可以直接通过键来获取或修改对应的值。例如,map::operator[]可以用来查找或插入键值对,如果键不存在,则会插入一个新的键值对并返回引用到新插入的值。

std::map<std::string, int> m;
m["apple"] = 1; // 插入键值对
int apple_count = m["apple"]; // 获取值,如果"apple"不存在则插入并初始化为0
  • 范围查询:通过lower_boundupper_bound可以轻松找出所有满足一定条件(比如年龄在某一范围内)的人群记录,这对于数据分析和统计十分有用。

  • 高效迭代:由于树形结构的特性,即使在大数据量下,通过迭代器遍历容器也能保持相对较高的性能。

  • 空间效率:虽然红黑树保证了log(n)级别的操作效率,但相比vector等线性容器,树形容器的空间利用率较低,尤其是在元素数量较少时。因此,在权衡空间和时间效率时,需要考虑具体的应用场景。

  • 并发安全:C++ STL中的这些容器都不是线程安全的,如果需要在多线程环境中使用,需自行添加同步机制或考虑使用C++17引入的std::unordered_map/std::unordered_set配合std::mutex等工具,或者使用第三方库提供的线程安全版本容器。

总之,熟练掌握C++ STL中的树形关联容器,不仅能提升代码的简洁度和执行效率,还能帮助开发者更好地应对各种数据组织和处理的需求。

实际使用例子:

当然,以下是一些关于C++中set、multiset、map和multimap的实际使用例子:

1. std::set的例子

#include <iostream>
#include <set>

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

    // 插入元素
    numbers.insert(5);
    numbers.insert(3);
    numbers.insert(1);
    numbers.insert(4);
    numbers.insert(2);

    // 打印集合中的所有元素(自动排序)
    for (const auto& num : numbers) {
        std::cout << num << ' ';
    }
    // 输出:1 2 3 4 5

    // 查找元素
    if (numbers.find(3) != numbers.end()) {
        std::cout << "3 is in the set.\n";
    }

    // 删除元素
    numbers.erase(3);
    
    return 0;
}

2. std::multiset的例子

#include <iostream>
#include <multiset>

int main() {
    std::multiset<int> multipleNumbers;

    // 插入重复元素
    multipleNumbers.insert(3);
    multipleNumbers.insert(1);
    multipleNumbers.insert(2);
    multipleNumbers.insert(2);
    multipleNumbers.insert(3);

    // 打印集合中的所有元素(自动排序)
    for (const auto& num : multipleNumbers) {
        std::cout << num << ' ';
    }
    // 输出:1 2 2 3 3

    // 计算特定元素的数量
    auto count = std::count(multipleNumbers.begin(), multipleNumbers.end(), 2);
    std::cout << "\nNumber of 2s: " << count << '\n';

    return 0;
}

3. std::map的例子

#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> studentGrades;

    // 插入键值对
    studentGrades["Alice"] = 85;
    studentGrades["Bob"] = 90;
    studentGrades["Charlie"] = 88;

    // 访问并修改值
    studentGrades["Alice"] += 5;

    // 打印所有学生的成绩
    for (const auto& pair : studentGrades) {
        std::cout << pair.first << ": " << pair.second << '\n';
    }
    // 输出:Alice: 90 Bob: 90 Charlie: 88

    // 查找学生并打印成绩
    if (auto it = studentGrades.find("Bob"); it != studentGrades.end()) {
        std::cout << "Bob's grade: " << it->second << '\n';
    }

    return 0;
}

4. std::multimap的例子

#include <iostream>
#include <multimap>

int main() {
    std::multimap<std::string, int> studentCourses;

    // 插入多个相同的键值对
    studentCourses.insert({"Alice", 101});
    studentCourses.insert({"Bob", 101});
    studentCourses.insert({"Alice", 102});
    studentCourses.insert({"Charlie", 101});

    // 打印Alice参加的所有课程
    for (const auto& pair : studentCourses.equal_range("Alice")) {
        std::cout << pair.first << ": " << pair.second << '\n';
    }
    // 输出:Alice: 101 Alice: 102

    return 0;
}

python推荐学习汇总连接:
50个开发必备的Python经典脚本(1-10)

50个开发必备的Python经典脚本(11-20)

50个开发必备的Python经典脚本(21-30)

50个开发必备的Python经典脚本(31-40)

50个开发必备的Python经典脚本(41-50)
————————————————

​最后我们放松一下眼睛
在这里插入图片描述

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极致人生-010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值