map和multimap

键值对容器

map 概述

map 是基于红黑树实现的键值对容器,特点是:

常用场景

  • 字典:单词到解释的映射
  • 计数:统计元素出现次数
  • 快速查找:通过键快速获取值
  • 离散化:将值映射到连续的区间

map 基本用法


#include <map>
using namespace std;

int main() {
    // 创建 map
    map mp;

    // 插入元素
    mp["apple"] = 5;
    mp["banana"] = 3;
    mp["cherry"] = 7;

    // 插入的其他方式
    mp.insert({"durian", 2});
    mp.insert(make_pair("elderberry", 4));

    // 访问元素
    cout << mp["apple"] << endl;  // 5

    // 检查键是否存在
    if (mp.find("banana") != mp.end()) {
        cout << "找到 banana" << endl;
    }

    // count 返回 0 或 1
    if (mp.count("fig")) {
        cout << "存在 fig" << endl;
    }

    // 删除元素
    mp.erase("apple");

    // 遍历
    for (auto& p : mp) {
        cout << p.first << ": " << p.second << endl;
    }

    // 大小操作
    cout << mp.size() << endl;   // 4
    cout << mp.empty() << endl;  // false
    mp.clear();

    return 0;
}
                

map 可视化

map 存储键值对,按键自动排序:

map 为空

map 高级操作

1. 获取边界键


map mp = {
    {1, "one"},
    {3, "three"},
    {5, "five"},
    {7, "seven"},
    {9, "nine"}
};

// 最小和最大键
cout << mp.begin()->first << endl;   // 1
cout << mp.rbegin()->first << endl;  // 9

// 大于等于某个键的第一个元素
auto it = mp.lower_bound(4);
// it 指向 {5, "five"}

// 大于某个键的第一个元素
it = mp.upper_bound(5);
// it 指向 {7, "seven"}

// 删除范围内的键值对
mp.erase(mp.lower_bound(3), mp.upper_bound(7));
// mp: {1, "one"}, {9, "nine"}
                

2. 自定义排序


// 降序排列
map> mp = {
    {1, "one"},
    {3, "three"},
    {2, "two"}
};
// mp: {3, "three"}, {2, "two"}, {1, "one"}

// 自定义比较函数
struct cmp {
    bool operator()(string a, string b) const {
        return a.length() < b.length();  // 按长度排序
    }
};

map mp2;
mp2["apple"] = 1;
mp2["pear"] = 2;
mp2["banana"] = 3;
// mp2: {"pear", 2}, {"apple", 1}, {"banana", 3}
                

3. 访问不存在的键


map mp;

// [] 操作符:不存在会自动创建默认值
int val = mp["apple"];  // mp["apple"] 被创建,值为 0
cout << val << endl;  // 0
cout << mp["apple"] << endl;  // 0

// 使用 at():不存在会抛出异常
try {
    int val2 = mp.at("banana");  // 抛出异常
} catch (const out_of_range& e) {
    cout << "键不存在" << endl;
}

// 查找后再访问
auto it = mp.find("banana");
if (it != mp.end()) {
    cout << it->second << endl;
}
                

multimap 概述

multimap 与 map 类似,但允许键重复。


#include <map>
using namespace std;

int main() {
    multimap mm;

    // 插入元素(允许重复键)
    mm.insert({"apple", 1});
    mm.insert({"banana", 2});
    mm.insert({"apple", 3});
    mm.insert({"apple", 4});

    // count 返回键的出现次数
    cout << mm.count("apple") << endl;  // 3

    // find 返回第一个匹配的迭代器
    auto it = mm.find("apple");  // 指向第一个 apple

    // 删除指定键的所有元素
    mm.erase("apple");
    // mm: {"banana", 2}

    // 删除指定位置的元素
    it = mm.find("banana");
    mm.erase(it);
    // mm: 空

    // 遍历所有相同键的元素
    mm.insert({"apple", 1});
    mm.insert({"apple", 2});
    mm.insert({"apple", 3});

    auto range = mm.equal_range("apple");
    for (auto it = range.first; it != range.second; it++) {
        cout << it->first << ": " << it->second << endl;
    }
    // 输出:
    // apple: 1
    // apple: 2
    // apple: 3

    return 0;
}
                

map vs multimap

  • map:键唯一,count() 返回 0 或 1
  • multimap:允许重复键,count() 返回实际个数
  • 访问方式:map 可以用 [],multimap 不支持

实际应用示例

1. 统计元素频率


map freq;
vector v = {1, 2, 2, 3, 3, 3, 4};

for (int x : v) {
    freq[x]++;
}

for (auto& p : freq) {
    cout << p.first << ": " << p.second << endl;
}
// 输出:
// 1: 1
// 2: 2
// 3: 3
// 4: 1
                

2. 离散化

 " << mp[x] << endl;
}
// 输出:
// 100 -> 2
// 50 -> 1
// 300 -> 4
// 50 -> 1
// 100 -> 2
// 200 -> 3
                

3. 区间查询


map mp = {{1, 10}, {3, 30}, {5, 50}, {7, 70}, {9, 90}};

// 查找键在 [3, 7] 范围内的所有键值对
auto start = mp.lower_bound(3);
auto end = mp.upper_bound(7);

for (auto it = start; it != end; it++) {
    cout << it->first << ": " << it->second << endl;
}
// 输出:
// 3: 30
// 5: 50
// 7: 70
                

4. 查找前驱和后继


map mp = {{1, "one"}, {3, "three"}, {5, "five"}, {7, "seven"}};

// 查找键为4的前驱(小于4的最大键)
auto it = mp.lower_bound(4);
if (it != mp.begin()) {
    it--;  // 前驱
    cout << "前驱: " << it->first << endl;  // 3
}

// 查找键为4的后继(大于4的最小键)
it = mp.upper_bound(4);
if (it != mp.end()) {
    cout << "后继: " << it->first << endl;  // 5
}
                

复杂度对比

操作 map/multimap unordered_map
插入$$ O(\log n) $$$$ O(1) $$
删除$$ O(\log n) $$$$ O(1) $$
查找$$ O(\log n) $$$$ O(1) $$
区间查询$$ O(\log n) $$不支持
顺序遍历有序无序

选择建议

  • 需要有序:使用 map
  • 不需要有序,追求速度:使用 unordered_map
  • 需要区间查询:使用 map(lower_bound/upper_bound)
  • 键可以重复:使用 multimap