Set 和 Map 深入详解及其区别

目录

关联式容器

键值对

树形结构的关联式容器

Set

核心概念

set的使用

set的模板参数列表

set的基本操作

集合运算

自定义比较函数

std::multiset

Map

核心概念

map的使用

map的模板参数​编辑

map的基本操作

查找操作

自定义比较函数

std::multimap

[Map 和 Set 的详细对比](#Map 和 Set 的详细对比)

本质区别:存储内容

简单对比表格:


关联式容器

在C++初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、 forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面 存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?

关联式容器 也是用来存储数据的,与序列式容器不同的是,其里面存储的是结构的 键值对,在数据检索时比序列式容器效率更高

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代 表键值,value表示与key对应的信息.

树形结构的关联式容器

根据应用场景的不同,STL总共实现了两种不同结构的关联式容器:树型结构与哈希结构。树型结 构的关联式容器主要有四种:map、set、multimap、multiset 。这四种容器的共同点是:使 用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一主要介绍Set和Map.

Set

核心概念

Set 是唯一值的集合,类似于数学中的集合:

  • 值唯一,不允许重复(因此可以使用set进行去重)
  • 保持插入顺序
  • 提供集合运算方法
  • 判断值存在性比数组高效
  • set在底层是用二叉搜索树(红黑树)实现

需要注意的是:

  1. 与map/multimap不同,map/multimap中存储的是真正的键值对,set中只放 value,但在底层实际存放的是由构成的键值对。
  2. set中的元素不允许修改
  3. set中的元素默认按照小于来比较
  4. Set 的实现相对简单,本质上可以看作一个键与值相同的 Map
set的使用
set的模板参数列表
  1. T: set中存放元素的类型,实际在底层存储的键值对。
  2. Compare:set中元素默认按照小于来比较
  3. Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
set的基本操作
cpp 复制代码
#include <set>
#include <iostream>

int main() {
    // 创建
    set<int> s1 = {3, 1, 4, 1, 5, 9};  // {1, 3, 4, 5, 9}
    
    // 插入
    s1.insert(2);
    s1.insert(3);  // 重复,不会插入
    
    auto result = s1.insert(6);
    if (result.second) {
        cout << "Insert succeeded" << endl;
    }
    
    // 删除
    s1.erase(4);                    // 删除值为4的元素
    auto it = s1.find(5);
    if (it != s1.end()) {
        s1.erase(it);
    }
    
    // 查找
    if (s1.find(3) != s1.end()) {
        cout << "3 found in set" << endl;
    }
    
    // 计数(总是0或1)
    cout << "Count of 3: " << s1.count(3) << endl;
    
    // 遍历
    for (const auto& val : s1) {
        cout << val << " ";
    }
    cout << endl;
    
    return 0;
}
集合运算
cpp 复制代码
set<int> setA = {1, 2, 3, 4, 5};
set<int> setB = {3, 4, 5, 6, 7};

// 并集
set<int> unionSet;
set_union(setA.begin(), setA.end(),
          setB.begin(), setB.end(),
          inserter(unionSet, unionSet.begin()));

// 交集
set<int> intersectionSet;
set_intersection(setA.begin(), setA.end(),
                setB.begin(), setB.end(),
                inserter(intersectionSet, intersectionSet.begin()));

// 差集
set<int> differenceSet;
set_difference(setA.begin(), setA.end(),
               setB.begin(), setB.end(),
               inserter(differenceSet, differenceSet.begin()));

// 对称差集
set<int> symmetricDifference;
set_symmetric_difference(setA.begin(), setA.end(),
                        setB.begin(), setB.end(),
                        inserter(symmetricDifference, symmetricDifference.begin()));
自定义比较函数
cpp 复制代码
// 按字符串长度排序
struct LengthCompare {
    bool operator()(const string& a, const string& b) const {
        if (a.length() == b.length()) {
            return a < b;  // 长度相同按字典序
        }
        return a.length() < b.length();
    }
};

set<string, LengthCompare> lengthSet;
lengthSet.insert("apple");
lengthSet.insert("banana");
lengthSet.insert("cat");
lengthSet.insert("dog");
// 顺序: cat, dog, apple, banana
std::multiset

简单来说就是允许重复值的set

Map

核心概念
  • 基于红黑树实现
  • 键值对存储(pair<const Key, T>)
  • map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
  • 键唯一,自动排序(在内部,map中的元素总是按照键值key进行比较排序)
  • 插入/删除/查找:O(log n)
map的使用
map的模板参数
  1. key: 键值对中key的类型
  2. T: 键值对中value的类型
  3. Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比 较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户 自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
  4. Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的 空间配置器
map的基本操作
cpp 复制代码
// 1. 默认构造函数
    map<int, string> map1;
    
// 2. 使用初始化列表
    map<int, string> map2 = {
        {1, "Alice"},
        {2, "Bob"},
        {3, "Charlie"}
    };
    
// 3. 范围构造函数
    map<int, string> map3(map2.begin(), map2.end());
    
// 4. 拷贝构造函数
    map<int, string> map4(map2);
/////////////////////////////////////////////////////////////////
// 插入元素
map<int, string> m;

// 1. insert() 方法
m.insert({1, "Alice"});                // C++11
m.insert(make_pair(2, "Bob"));         // C++98
m.insert(pair<int, string>(3, "Charlie"));

// 2. emplace() 方法(C++11,效率更高)
m.emplace(4, "David");  // 直接构造,避免临时对象

// 3. [] 操作符(如果键不存在会创建)
m[5] = "Eve";           // 插入或修改
m[1] = "Alice Updated"; // 修改已存在的键

// 访问元素
cout << m[1] << endl;           // 如果键不存在会创建默认值
cout << m.at(1) << endl;        // 如果键不存在抛出 out_of_range 异常

// 检查元素是否存在
if (m.find(1) != m.end()) {
    cout << "Key 1 exists" << endl;
}

// 删除元素
m.erase(1);                    // 删除键为1的元素
auto it = m.find(2);
if (it != m.end()) {
    m.erase(it);               // 通过迭代器删除
}
m.erase(m.begin(), m.end());   // 删除范围
m.clear();                     // 清空所有

// 大小和容量
cout << "Size: " << m.size() << endl;
cout << "Empty: " << m.empty() << endl;
cout << "Max size: " << m.max_size() << endl;
查找操作
cpp 复制代码
map<int, string> m = {{1, "A"}, {2, "B"}, {3, "C"}};

// 1. find() - O(log n)
auto it = m.find(2);
if (it != m.end()) {
    cout << "Found: " << it->second << endl;
}

// 2. count() - 返回键的数量(对于map总是0或1)
if (m.count(2) > 0) {
    cout << "Key 2 exists" << endl;
}

// 3. lower_bound() - 第一个不小于key的元素
auto lb = m.lower_bound(2);
if (lb != m.end()) {
    cout << "Lower bound: " << lb->first << endl;
}

// 4. upper_bound() - 第一个大于key的元素
auto ub = m.upper_bound(2);
if (ub != m.end()) {
    cout << "Upper bound: " << ub->first << endl;
}

// 5. equal_range() - 返回pair<lower_bound, upper_bound>
auto range = m.equal_range(2);
for (auto it = range.first; it != range.second; ++it) {
    cout << it->first << ": " << it->second << endl;
}
自定义比较函数
cpp 复制代码
// 1. 函数对象
struct Compare {
    bool operator()(const string& a, const string& b) const {
        return a.length() < b.length();  // 按长度排序
    }
};

map<string, int, Compare> lengthMap;
lengthMap["aaa"] = 1;
lengthMap["bb"] = 2;
lengthMap["c"] = 3;

// 2. 使用lambda表达式(C++11)
auto cmp = [](const string& a, const string& b) {
    return a.length() < b.length();
};
map<string, int, decltype(cmp)> lambdaMap(cmp);

// 3. 降序排列
map<int, string, greater<int>> descMap;
descMap[1] = "A";
descMap[2] = "B";
descMap[3] = "C";  // 顺序:3, 2, 1
std::multimap

简单来说就是允许重复键的map

Map 和 Set 的详细对比

本质区别:存储内容

Map(映射):

Map 存储的是键值对,就像一个字典。每个元素都是一个对组,包含键和值两部分。键是唯一的标识符,值是与该键相关联的数据。你可以通过键快速查找到对应的值,就像通过单词查找字典中的解释一样。

Set(集合):

Set 存储的是唯一的值,就像一个数学集合。它只关心值本身,不存储额外的关联信息。Set 的主要目的是确保集合中的元素是唯一的,并快速检查某个值是否存在于集合中。

简单来说:

Map 是关系的容器,它建立键和值之间的映射。它回答的问题是:"对于这个键,对应的值是什么?" Map 强调两个不同实体之间的连接,适合存储丰富的关联信息。

Set 是元素的容器,它维护一个唯一值的集合。它回答的问题是:"这个值是否存在?" Set 强调元素本身和集合的数学性质,适合去重、存在性检查和集合运算。

简单对比表格:

|-----------|-------------------------|--------------------|
| 维度 | Map(映射) | Set(集合) |
| 存储内容 | 键值对(key-value pairs) | 唯一值(unique values) |
| 数据结构 | 字典/映射表 | 数学集合 |
| 核心用途 | 建立键到值的映射关系 | 维护不重复的元素集合 |
| 典型问题 | "键X对应的值是什么?" | "值X是否存在?" |
| C++示例 | std::map<int, string> | std::set<int> |

相关推荐
小尧嵌入式2 小时前
Linux进程线程与进程间通信
linux·运维·服务器·c语言·开发语言·数据结构·microsoft
SmoothSailingT2 小时前
C/C++——结构体(Struct)
开发语言·c++·结构体·struct
Lucis__2 小时前
红黑树实现—规则&约束的平衡之道
数据结构·c++·算法·红黑树
名誉寒冰2 小时前
深入理解fd_set:从基础到实战应用(Linux/C++)
java·linux·c++
智者知已应修善业2 小时前
【字符串提取3个整数求和】2024-2-11
c语言·c++·经验分享·笔记·算法
博语小屋3 小时前
Linux 地址转换函数详解
linux·运维·服务器·c++
特立独行的猫a3 小时前
C++开发中的构建工具:现代CMake实战速成
开发语言·c++·cmake·入门教程
听*雨声3 小时前
09_软考_数据结构
数据结构
dragoooon343 小时前
[hot100 NO.31~36]
数据结构·算法·排序算法