C++: map和set

目录

set

multiset

map

mulimap

pair


关联式容器与序列式容器

序列式容器 (Sequence Containers)

  • 关注元素的位置和插入顺序

  • 典型的"先来后到"顺序存储

关联式容器 (Associative Containers)

  • 关注元素之间的关联关系

  • 基于键值对(key-value)的快速查找

具体容器类型对比

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

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

SGI-STL中关于键值对的定义:

根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。

树型结构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果。

set

set的介绍

set的模板参数列表

T:set中存放元素的类型,实际在底层存储<value, value>的键值对

Compare: set中元素默认按照小于来比较

Alloc: set中元素空间的管理方式,使用STL提供的空间配置管理器

set的构造

set容器常见的接口

++基本构造和赋值:operator= 赋值操作++

cpp 复制代码
#include <iostream>
#include <set>
using namespace std;

void testAssignment() {
    set<int> s1 = {1, 3, 5, 7, 9};
    set<int> s2;
    
    s2 = s1;  // 赋值操作
    cout << "s2 after assignment: ";
    for (int x : s2) cout << x << " ";  // 1 3 5 7 9
    cout << endl;
    
    // 移动赋值
    set<int> s3 = move(s2);
    cout << "s3 after move: ";
    for (int x : s3) cout << x << " ";  // 1 3 5 7 9
    cout << endl;
    cout << "s2 size after move: " << s2.size() << endl;  // 0
}

++迭代器接口++:正向和反向迭代器

cpp 复制代码
void testIterators() {
    set<int> s = {10, 20, 30, 40, 50};
    
    cout << "正向遍历: ";
    for (auto it = s.begin(); it != s.end(); ++it) {
        cout << *it << " ";  // 10 20 30 40 50
    }
    cout << endl;
    
    cout << "反向遍历: ";
    for (auto it = s.rbegin(); it != s.rend(); ++it) {
        cout << *it << " ";  // 50 40 30 20 10
    }
    cout << endl;
    
    // C++11引入的const迭代器
    cout << "常量正向遍历: ";
    for (auto it = s.cbegin(); it != s.cend(); ++it) {
        // *it = 100;  // 错误:不能修改常量迭代器指向的值
        cout << *it << " ";
    }
    cout << endl;
}

++容量查询++:empty() 和 size()

cpp 复制代码
void testCapacity() {
    set<int> s;
    cout << "空set - empty: " << s.empty() << ", size: " << s.size() << endl;
    
    s.insert({1, 2, 3});
    cout << "插入后 - empty: " << s.empty() << ", size: " << s.size() << endl;
}

++insert()++ 插入操作

++set::insert 的返回值是一个 pair类型++,具体定义为:

这是一个模板类,包含两个成员:

  • first: 迭代器(iterator),指向被插入的元素

  • second: 布尔值(bool),表示插入是否成功

cpp 复制代码
void testInsert() {
    set<int> s;
    
    // 方式1:直接插入值
    auto result1 = s.insert(10);
    cout << "插入10: " << (result1.second ? "成功" : "失败") 
         << ", 插入位置的值: " << *result1.first << endl;
    // result1 的类型: pair<set<int>::iterator, bool>
    // result1.first: 指向元素10的迭代器
    // result1.second: true (因为10不存在,插入成功)
    
    // 方式2:插入已存在值(会失败)
    auto result2 = s.insert(10);
    cout << "再次插入10: " << (result2.second ? "成功" : "失败") << endl;
    
    // 方式3:批量插入
    s.insert({5, 15, 25, 35});
    
    cout << "最终set内容: ";
    for (int x : s) cout << x << " ";  // 5 10 15 25 35
    cout << endl;
}

++erase()++删除操作

cpp 复制代码
void testErase() {
    set<int> s = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    // 方式1:通过值删除
    size_t count1 = s.erase(5);  // 返回删除的元素个数
    cout << "删除值5,删除了 " << count1 << " 个元素" << endl;
    
    // 方式2:通过迭代器删除
    auto it = s.find(3);
    if (it != s.end()) {
        s.erase(it);  // 安全删除
        cout << "通过迭代器删除3" << endl;
    }
    
    // 方式3:删除不存在的值(不会报错)
    size_t count2 = s.erase(100);
    cout << "删除不存在的值100,删除了 " << count2 << " 个元素" << endl;
    
    // 方式4:删除范围
    auto first = s.find(6);
    auto last = s.find(8);
    if (first != s.end() && last != s.end()) {
        s.erase(first, last);  // 删除[6, 8),注意是前闭后开
    }
    
    cout << "删除后set内容: ";
    for (int x : s) cout << x << " ";  // 1 2 4 8 9
    cout << endl;
}

返回值类型总结

++查找操作++(核心功能):find() 查找

cpp 复制代码
void testFind() {
    set<int> s = {10, 20, 30, 40, 50};
    
    // set专用的find(二分查找 O(log n))
    auto it = s.find(30);
    if (it != s.end()) {
        cout << "找到30: " << *it << endl;
    } else {
        cout << "未找到30" << endl;
    }
    
    // 与std::find对比(线性查找 O(n))
    auto it2 = find(s.begin(), s.end(), 30);
    if (it2 != s.end()) {
        cout << "std::find也找到30: " << *it2 << endl;
    }
    
    // 查找不存在的值
    auto it3 = s.find(100);
    if (it3 == s.end()) {
        cout << "100不存在于set中" << endl;
    }
}

++lower_bound() 和 upper_bound()++

cpp 复制代码
void testBounds() {
    set<int> s = {10, 20, 30, 40, 50};
    
    // lower_bound: 第一个 >= 值的元素
    auto lb = s.lower_bound(25);
    if (lb != s.end()) {
        cout << "lower_bound(25): " << *lb << endl;  // 30
    }
    
    // upper_bound: 第一个 > 值的元素  
    auto ub = s.upper_bound(25);
    if (ub != s.end()) {
        cout << "upper_bound(25): " << *ub << endl;  // 30
    }
    
    // 查找边界值
    cout << "lower_bound(30): " << *s.lower_bound(30) << endl;  // 30
    cout << "upper_bound(30): " << *s.upper_bound(30) << endl;  // 40
    
    // 查找超出范围的值
    auto beyond = s.upper_bound(60);
    if (beyond == s.end()) {
        cout << "upper_bound(60) 返回end()" << endl;
    }
}

++equal_range()++相等范围

cpp 复制代码
void testEqualRange() {
    set<int> s = {10, 20, 30, 40, 50};
    
    // equal_range返回pair<lower_bound, upper_bound>
    auto range = s.equal_range(25);
    cout << "equal_range(25): [" << *range.first << ", " << *range.second << ")" << endl;
    // 输出: [30, 30) 因为25不在set中
    
    // 对于存在的值
    range = s.equal_range(30);
    cout << "equal_range(30): [" << *range.first << ", " << *range.second << ")" << endl;
    // 输出: [30, 40)
    
    // 可以用来遍历某个范围内的元素
    cout << "20到40之间的元素: ";
    range = s.equal_range(20);
    range.second = s.upper_bound(40);  // 手动调整上界
    for (auto it = range.first; it != range.second; ++it) {
        cout << *it << " ";  // 20 30 40
    }
    cout << endl;
}

++count()++计数

cpp 复制代码
void testCount() {
    set<int> s = {1, 2, 3, 4, 5};
    
    // set中count只能返回0或1(因为元素唯一)
    cout << "count(3): " << s.count(3) << endl;  // 1
    cout << "count(10): " << s.count(10) << endl; // 0
    
    // 与multiset对比
    multiset<int> ms = {1, 2, 2, 3, 3, 3};
    cout << "multiset count(2): " << ms.count(2) << endl;  // 2
    cout << "multiset count(3): " << ms.count(3) << endl;  // 3
}

++swap() 和 clear()++

cpp 复制代码
void testSwapClear() {
    set<int> s1 = {1, 3, 5};
    set<int> s2 = {2, 4, 6};
    
    cout << "交换前 - s1: ";
    for (int x : s1) cout << x << " ";  // 1 3 5
    cout << ", s2: ";
    for (int x : s2) cout << x << " ";  // 2 4 6
    cout << endl;
    
    s1.swap(s2);  // 交换两个set的内容
    
    cout << "交换后 - s1: ";
    for (int x : s1) cout << x << " ";  // 2 4 6
    cout << ", s2: ";
    for (int x : s2) cout << x << " ";  // 1 3 5
    cout << endl;
    
    s1.clear();  // 清空set
    cout << "清空后s1的size: " << s1.size() << endl;  // 0
}

完整测试代码

cpp 复制代码
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;

int main() {
    cout << "=== set容器接口测试 ===" << endl;
    
    testAssignment();
    testIterators(); 
    testCapacity();
    testInsert();
    testErase();
    testFind();
    testBounds();
    testEqualRange();
    testCount();
    testSwapClear();
    
    // 验证自动排序和去重特性
    cout << "\n=== 自动排序和去重验证 ===" << endl;
    set<int> s = {5, 2, 8, 2, 5, 1, 8, 9};
    cout << "插入{5,2,8,2,5,1,8,9}后: ";
    for (int x : s) cout << x << " ";  // 1 2 5 8 9
    cout << endl;
    
    return 0;
}

重要特性总结

  1. 自动排序和去重
cpp 复制代码
set<int> s = {3, 1, 4, 1, 5, 9, 2, 6};
// 实际存储: 1 2 3 4 5 6 9(自动排序+去重)
  1. 查找性能对比
cpp 复制代码
set<int> large_set;
// 插入大量数据...

// 专用find - O(log n) - 推荐
auto it1 = large_set.find(target);

// 通用find - O(n) - 不推荐  
auto it2 = find(large_set.begin(), large_set.end(), target);
  1. 安全删除模式
cpp 复制代码
// 安全删除模式
auto it = s.find(value);
if (it != s.end()) {
    s.erase(it);  // 安全,迭代器有效才删除
}

// 直接删除值(更简洁)
s.erase(value);  // 如果值不存在,不会报错

使用建议

  1. 需要快速查找时优先使用set专用find

  2. 需要范围查询时使用lower_bound/upper_bound/equal_range

  3. 删除元素时优先使用值删除,避免迭代器失效问题

  4. 需要保持插入顺序时不要使用set,选择vector或list

  5. 需要重复元素时使用multiset

set的这些特性使其成为需要快速查找、自动排序和唯一元素的理想选择!

multiset

multiset 确实与 set 的主要区别就是允许重复元素,其他接口基本一致。

++1.++

1.1 基本特性对比

cpp 复制代码
void basicDifference() {
    cout << "=== set vs multiset 基本区别 ===" << endl;
    
    // set: 自动去重
    set<int> s = {1, 2, 2, 3, 3, 3};
    cout << "set 内容: ";
    for (int x : s) cout << x << " ";  // 1 2 3
    cout << ", 大小: " << s.size() << endl;  // 3
    
    // multiset: 允许重复
    multiset<int> ms = {1, 2, 2, 3, 3, 3};
    cout << "multiset 内容: ";
    for (int x : ms) cout << x << " ";  // 1 2 2 3 3 3
    cout << ", 大小: " << ms.size() << endl;  // 6
}

1.2 insert() 行为的区别

cpp 复制代码
void testInsertDifference() {
    cout << "\n=== insert() 行为区别 ===" << endl;
    
    set<int> s;
    multiset<int> ms;
    
    // set插入重复元素
    auto s_result1 = s.insert(10);
    cout << "set第一次插入10: " << (s_result1.second ? "成功" : "失败") << endl;  // 成功
    
    auto s_result2 = s.insert(10);
    cout << "set第二次插入10: " << (s_result2.second ? "成功" : "失败") << endl;  // 失败
    
    // multiset插入重复元素
    auto ms_result1 = ms.insert(10);
    cout << "multiset第一次插入10: 成功" << endl;
    
    auto ms_result2 = ms.insert(10);
    cout << "multiset第二次插入10: 成功" << endl;
    
    cout << "set大小: " << s.size() << endl;     // 1
    cout << "multiset大小: " << ms.size() << endl; // 2
}

++2. multiset 特有的应用场景++

2.1 count() 的实际应用

cpp 复制代码
void testCountUsage() {
    cout << "\n=== count() 的实际应用 ===" << endl;
    
    // 成绩统计示例
    multiset<int> scores = {85, 92, 78, 85, 90, 85, 78, 95};
    
    cout << "所有成绩: ";
    for (int score : scores) cout << score << " ";
    cout << endl;
    
    // 统计每个分数出现的次数
    cout << "成绩统计:" << endl;
    auto it = scores.begin();
    while (it != scores.end()) {
        int current_score = *it;
        int count = scores.count(current_score);
        cout << "分数 " << current_score << ": " << count << " 次" << endl;
        
        // 跳过相同的分数
        advance(it, count);
    }
    
    // 查找特定分数的出现次数
    int target_score = 85;
    cout << "分数 " << target_score << " 出现了 " 
         << scores.count(target_score) << " 次" << endl;
}

2.2 equal_range() 的实际应用

cpp 复制代码
void testEqualRangeUsage() {
    cout << "\n=== equal_range() 的实际应用 ===" << endl;
    
    multiset<int> data = {10, 20, 20, 20, 30, 30, 40, 50, 50, 50};
    
    // 查找所有等于20的元素
    auto range = data.equal_range(20);
    cout << "所有等于20的元素: ";
    for (auto it = range.first; it != range.second; ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // 查找范围 [30, 50) 的元素
    auto lower = data.lower_bound(30);  // 第一个 >= 30 的元素
    auto upper = data.upper_bound(49); // 第一个 > 49 的元素
    
    cout << "范围 [30, 50) 的元素: ";
    for (auto it = lower; it != upper; ++it) {
        cout << *it << " ";  // 30, 30, 40
    }
    cout << endl;
    
    // 使用equal_range处理不存在的值
    auto not_found_range = data.equal_range(25);
    if (not_found_range.first == not_found_range.second) {
        cout << "元素25不存在" << endl;
    }
}

++3. multiset 专有操作示例++

3.1 删除特定数量的重复元素

cpp 复制代码
void testMultisetErase() {
    cout << "\n=== multiset 特有的删除操作 ===" << endl;
    
    multiset<int> ms = {1, 2, 2, 2, 3, 3, 4, 4, 4, 4};
    
    cout << "原始multiset: ";
    for (int x : ms) cout << x << " ";
    cout << ", 大小: " << ms.size() << endl;
    
    // 删除所有值为2的元素
    size_t removed = ms.erase(2);
    cout << "删除所有2,删除了 " << removed << " 个元素" << endl;
    
    cout << "删除后: ";
    for (int x : ms) cout << x << " ";  // 1 3 3 4 4 4 4
    cout << endl;
    
    // 只删除第一个3
    auto it = ms.find(3);
    if (it != ms.end()) {
        ms.erase(it);  // 只删除迭代器指向的那个3
    }
    
    cout << "删除一个3后: ";
    for (int x : ms) cout << x << " ";  // 1 3 4 4 4 4
    cout << endl;
}

删除某个数字,就是删除所有这个数字。

3.2 处理重复元素的迭代技巧

cpp 复制代码
void testIterationTechniques() {
    cout << "\n=== 处理重复元素的迭代技巧 ===" << endl;
    
    multiset<string> words = {"apple", "banana", "apple", "cherry", "apple", "date"};
    
    // 方法1:使用equal_range遍历特定值的所有出现
    cout << "所有'apple'的出现: ";
    auto apple_range = words.equal_range("apple");
    for (auto it = apple_range.first; it != apple_range.second; ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // 方法2:跳过重复元素的遍历
    cout << "不重复遍历: ";
    auto it = words.begin();
    while (it != words.end()) {
        string current_word = *it;
        cout << current_word << "(" << words.count(current_word) << ") ";
        
        // 跳到下一个不同的单词
        it = words.upper_bound(current_word);
    }
    cout << endl;
}

使用建议

cpp 复制代码
void usageRecommendations() {
    cout << "\n=== 使用建议 ===" << endl;
    
    cout << "适合使用multiset的场景:" << endl;
    cout << "1. 需要维护排序且允许重复的集合" << endl;
    cout << "2. 需要频繁统计元素出现次数" << endl;
    cout << "3. 需要快速查找某个范围内的所有元素" << endl;
    cout << "4. 需要保持插入顺序但允许重复(与vector不同)" << endl;
    
    cout << "\n不适合使用multiset的场景:" << endl;
    cout << "1. 需要唯一元素 → 使用set" << endl;
    cout << "2. 需要极快插入且不关心顺序 → 使用unordered_multiset" << endl;
    cout << "3. 只需要简单集合操作 → 使用vector + sort" << endl;
}

完整测试代码

cpp 复制代码
int main()
{
	//cout << " === set容器接口测试 === " << endl;

	/*testAssignment();*/
   /* testAssignment();*/
    //testCapacity();
    /*testInsert();*/
    //testErase();
    /*testFind();*/
    /*testBounds();*/
    //testEqualRange();
    //testCount();
    //testSwapClear();
    basicDifference();
    testInsertDifference();
    testCountUsage();
    testEqualRangeUsage();
    testMultisetErase();
    testIterationTechniques();
    usageRecommendations();

    return 0;
}

multiset 的核心特点:

  1. 允许重复元素:与set最主要的区别

  2. 自动排序:保持元素有序

  3. 高效操作:插入、删除、查找都是O(log n)

  4. 特有功能:count()和equal_range()在multiset中特别有用

关键接口对比:

操作 set multiset
insert(value) 可能失败(重复时) 总是成功
count(value) 返回0或1 返回实际出现次数
equal_range(value) 范围最多1个元素 可能包含多个元素
erase(value) 删除0或1个元素 删除所有该值的元素

一句话总结 :当您需要维护一个有序且允许重复的集合时,multiset是最佳选择!

map

map的介绍

map与set的基本区别

cpp 复制代码
#include <iostream>
#include <map>
#include <set>
using namespace std;

void basicDifference() {
    cout << "=== map vs set 基本区别 ===" << endl;
    
    // set: 只存储键(key)
    set<string> s = {"apple", "banana", "cherry"};
    cout << "set内容: ";
    for (const auto& key : s) cout << key << " ";  // apple banana cherry
    cout << endl;
    
    // map: 存储键值对(key-value)
    map<string, int> m = {{"apple", 3}, {"banana", 2}, {"cherry", 5}};
    cout << "map内容: ";
    for (const auto& pair : m) {
        cout << pair.first << ":" << pair.second << " ";          

        // apple:3 banana:2 cherry:5
    }
    cout << endl;
}

map的模板参数列表

key: 键值对中key的类型

T: 键值对中value的类型

Compare: 比较器的类型。map中的元素是按照key来比较的,缺省情况下按照++小于++ 来比较,一般情况下(内置类型元素)该参数不需要传递。++如果无法比较时++ (自定义类型),++需要用户 自己显式传递比较规则++(一般情况下按照函数指针或者仿函数来传递)

Alloc:通过空间配置器来申请底层空间,不需要用户传递

注意:使用map时,需要包含头文件

map的构造

map的迭代器

map的容量与元素访问

问题:当key不在map中时,通过operator获取对应value时会发生什么问题?

cpp 复制代码
void testAutoInsertion() {
    map<string, int> scores = {{"Alice", 90}, {"Bob", 85}};
    
    cout << "操作前map大小: " << scores.size() << endl;  // 2
    
    // 访问不存在的key
    
    cout << "Charlie的分数: " << charlie_score << endl;  // 输出: 0
    cout << "操作后map大小: " << scores.size() << endl;  // 3 - 自动增加了!
    
}

operator[]调用等价于:

cpp 复制代码
(*((this->insert(make_pair(k, mapped_type()))).first)).second

让我们分解理解

cpp 复制代码
void explainEquivalentCode() {
    map<string, int> scores;
    
    // 分解operator["Charlie"]的等价操作
    auto result = scores.insert(make_pair("Charlie", int())); 
    // 1. 尝试插入pair("Charlie", 0)
    // 2. 如果key已存在,insert返回已存在元素的迭代器
    // 3. 如果key不存在,插入新元素并返回新元素的迭代器
    
    int& value_ref = (*(result.first)).second;
    // 获取映射值的引用
    
    cout << "插入结果: " << (result.second ? "成功" : "失败") << endl;
    cout << "获取的值: " << value_ref << endl;
}

当key不存在时,operator[]用默认 value与key构造键值对然后插入,返回该默认value。at()函数直接抛异常。

构造和初始化

cpp 复制代码
void testMapConstruction() {
    cout << "\n=== map的构造方式 ===" << endl;
    
    // 方式1:默认构造
    map<string, int> m1;
    
    // 方式2:初始化列表
    map<string, int> m2 = {
        {"Alice", 90},
        {"Bob", 85},
        {"Charlie", 92}
    };
    
    // 方式3:拷贝构造
    map<string, int> m3(m2);
    
    // 方式4:范围构造
    map<string, int> m4(m2.begin(), m2.end());
    
    cout << "m2内容: ";
    for (const auto& p : m2) {
        cout << p.first << ":" << p.second << " ";
    }
    cout << endl;
}

插入操作

cpp 复制代码
void testInsertOperations() {
    cout << "\n=== 插入操作对比 ===" << endl;

    map<string, int> m;

    // map插入:需要键值对
    auto m_result = m.insert({ "apple", 3 });  // 使用pair
    cout << "map插入结果: " << (m_result.second ? "成功" : "失败") << endl;

    for (const auto& p : m) {
        cout << p.first << ":" << p.second << " ";
    }
    cout << endl;
}

访问操作(map特有功能)

cpp 复制代码
void testAccessOperations() {
    cout << "\n=== 访问操作(map特有)===" << endl;
    
    map<string, int> scores = {{"Alice", 90}, {"Bob", 85}};
    
    // 1. operator[](最重要、最常用)
    cout << "Alice的分数: " << scores["Alice"] << endl;  // 90
    
    // operator[]的特殊行为:key不存在时自动插入
    cout << "Charlie的分数: " << scores["Charlie"] << endl;  // 0(自动插入)
    cout << "map大小: " << scores.size() << endl;  // 3
    
    // 2. at()方法(安全访问)
    try {
        cout << "Bob的分数: " << scores.at("Bob") << endl;  // 85
        cout << "David的分数: " << scores.at("David") << endl;  // 抛出异常
    } catch (const out_of_range& e) {
        cout << "异常: " << e.what() << endl;
    }
    
    // 3. 通过迭代器访问
    auto it = scores.find("Alice");
    if (it != scores.end()) {
        cout << "通过迭代器访问: " << it->first << ":" << it->second << endl;
    }
}

查找操作

cpp 复制代码
void testFindOperations() {
    cout << "\n=== 查找操作对比 ===" << endl;
    
    set<string> s = {"apple", "banana", "cherry"};
    map<string, int> m = {{"apple", 3}, {"banana", 2}, {"cherry", 5}};
    
    // set查找:返回键的迭代器
    auto s_it = s.find("banana");
    if (s_it != s.end()) {
        cout << "set找到: " << *s_it << endl;
    }
    
    // map查找:返回键值对的迭代器
    auto m_it = m.find("banana");
    if (m_it != m.end()) {
        cout << "map找到: " << m_it->first << ":" << m_it->second << endl;
    }
    
    // map特有的查找模式
    string key = "apple";
    if (m.count(key) > 0) {  // 检查键是否存在
        cout << key << "的值为: " << m[key] << endl;  // 安全访问
    }
}

删除操作

cpp 复制代码
void testEraseOperations() {
    cout << "\n=== 删除操作 ===" << endl;
    
    map<string, int> m = {
        {"A", 1}, {"B", 2}, {"C", 3}, {"D", 4}, {"E", 5}
    };
    
    // 1. 通过键删除
    size_t count = m.erase("B");  // 返回删除的元素个数
    cout << "删除了 " << count << " 个元素" << endl;
    
    // 2. 通过迭代器删除
    auto it = m.find("C");
    if (it != m.end()) {
        m.erase(it);
    }
    
    // 3. 删除范围
    auto first = m.find("D");
    if (first != m.end()) {
        m.erase(first, m.end());
    }
    
    cout << "删除后剩余: ";
    for (const auto& p : m) {
        cout << p.first << " ";
    }
    cout << endl;
}

map特有功能讲解

operator[] 的妙用

cpp 复制代码
void testOperatorBracket() {
    cout << "\n=== operator[] 的实用技巧 ===" << endl;
    
    // 1. 单词计数(经典用法)
    string text = "apple banana apple cherry banana apple";
    map<string, int> wordCount;
    
    stringstream ss(text);
    string word;
    while (ss >> word) {
        wordCount[word]++;  // 自动初始化为0然后递增
    }
    
    cout << "单词统计: ";
    for (const auto& p : wordCount) {
        cout << p.first << ":" << p.second << " ";
    }
    cout << endl;
    
    // 2. 默认值设置
    map<string, string> config;
    string value = config["timeout"];  // 自动插入空字符串
    cout << "timeout配置: '" << value << "'" << endl;
    
    // 3. 链式访问(需要谨慎)
    map<string, map<string, int>> nestedMap;
    nestedMap["user1"]["score"] = 100;  // 自动创建内层map
    cout << "嵌套访问: " << nestedMap["user1"]["score"] << endl;
}

范围查询(map特有优势)

cpp 复制代码
void testRangeQueries() {
    cout << "\n=== 范围查询 ===" << endl;
    
    map<int, string> students = {
        {101, "Alice"}, {102, "Bob"}, {105, "Charlie"}, 
        {108, "David"}, {110, "Eve"}
    };
    
    // 查找学号在[103, 109)范围内的学生
    auto lower = students.lower_bound(103);  // 第一个 >= 103
    auto upper = students.upper_bound(109);   // 第一个 > 109
    
    cout << "学号103-109的学生: ";
    for (auto it = lower; it != upper; ++it) {
        cout << it->first << ":" << it->second << " ";  // 105:Charlie 108:David
    }
    cout << endl;
    
    // equal_range获取相等键的范围(在multimap中更有用)
    auto range = students.equal_range(105);
    if (range.first != range.second) {
        cout << "学号105的学生: " << range.first->second << endl;
    }
}

mulimap

mulimap的介绍

mulimap的使用

operator[]的语义是"获取或设置指定键的值",但在multimap中,一个键对应多个值,这导致了根本性的语义歧义。所以multimap中没有重载operator[]操作。

cpp 复制代码
#include <iostream>
#include <map>
#include <string>
using namespace std;

void oneToManyMapping() {
    cout << "=== 一对多关系映射 ===" << endl;
    
    // 学生与课程成绩的映射
    multimap<string, string> studentCourses;
    
    // 添加选课关系
    studentCourses.insert({"Alice", "Math"});
    studentCourses.insert({"Alice", "Physics"});
    studentCourses.insert({"Bob", "Chemistry"});
    studentCourses.insert({"Alice", "Computer Science"});
    studentCourses.insert({"Bob", "Math"});
    
    // 查询Alice的所有课程
    cout << "Alice的课程: ";
    auto range = studentCourses.equal_range("Alice");
    for (auto it = range.first; it != range.second; ++it) {
        cout << it->second << " ";
    }
    cout << endl;
    
    // 查询Bob的所有课程
    cout << "Bob的课程: ";
    range = studentCourses.equal_range("Bob");
    for (auto it = range.first; it != range.second; ++it) {
        cout << it->second << " ";
    }
    cout << endl;
}

pair

想象一下 pair 就像一张名片:

  • 名片上有两个主要信息:姓名 + 电话号码

  • 这两个信息是相关的,总是一起出现

  • 但名片就是一个独立的东西

注意:头文件是utility。pair就是一个"数据对",可以把任意两个值打包在一起,不管它们是什么类型。

代码中的基本形式

cpp 复制代码
#include <iostream>
#include <utility>  // 包含pair的头文件
using namespace std;

int main() {
    // 创建一个pair:就像创建一张名片
    pair<string, string> businessCard = {"张三", "138-1234-5678"};
    
    // 访问pair中的信息
    cout << "姓名: " << businessCard.first << endl;    // 张三
    cout << "电话: " << businessCard.second << endl;   // 138-1234-5678
    
    return 0;
}

pair 与 map 的根本区别,map是由很多pair组成的。

相关推荐
大袁同学2 小时前
【二叉搜索树】:程序的“决策树”,排序数据的基石
数据结构·c++·算法·决策树·stl
郝学胜-神的一滴2 小时前
Qt QPushButton 样式完全指南:从基础到高级实现
linux·开发语言·c++·qt·程序人生
沐知全栈开发2 小时前
R MySQL 连接
开发语言
⠀One0ne2 小时前
【C++ 面试题】内存对齐
c++
tryxr2 小时前
变量捕获相关内容
java·开发语言·jvm
Elias不吃糖2 小时前
NebulaChat 框架学习笔记:原子变量与左值引用的工程应用
c++·学习
百锦再2 小时前
大型省级政务平台采用金仓数据库(KingbaseES)
开发语言·数据库·后端·rust·eclipse
biter down3 小时前
C 语言17:位操作符 & | ^:从二进制编码到大小端
c语言·开发语言
Theliars3 小时前
Ubuntu 上使用 VSCode 调试 C++ (CMake 项目) 指南
c++·vscode·ubuntu·cmake