map/multimap-deepseek 学案
wflengxuenong · · 个人记录
map/multimap运用
一、容器特性对比
| 特性 | map | multimap |
|---|---|---|
| 键唯一性 | 唯一 | 允许重复键 |
| 底层实现 | 红黑树 | 红黑树 |
| 插入复杂度 | O(log n) | O(log n) |
| 查找复杂度 | O(log n) | O(log n) |
| 迭代器类型 | 双向迭代器 | 双向迭代器 |
| 典型应用场景 | 字典、哈希表替代品 | 一对多关系存储 |
二、核心操作详解
1. 基本操作
#include <map>
using namespace std;
map<string, int> mp;
multimap<int, string> mmp;
// 插入元素
mp["Alice"] = 95; // 直接赋值
mp.insert({"Bob", 88}); // 插入pair对象
mmp.emplace(90, "Charlie"); // 原地构造
// 删除元素
mp.erase("Alice"); // 按key删除
mmp.erase(mmp.find(90)); // 按迭代器删除
// 查找操作
auto it = mp.find("Bob"); // 返回迭代器
int cnt = mmp.count(90); // 统计key出现次数
// 范围迭代
auto [start, end] = mmp.equal_range(85); // 获取键值范围
2. 高级操作
// 自定义排序规则
struct cmp {
bool operator()(const string& a, const string& b) const {
return a.length() < b.length();
}
};
map<string, int, cmp> lenMap;
// 合并map(C++17)
map<int, int> m1, m2;
m1.merge(m2); // 合并后m2保留冲突键值
// 节点操作(C++17)
auto nh = mp.extract("Alice"); // 提取节点
mp.insert(move(nh)); // 重新插入
三、经典例题解析
例题1:POJ2503 Babelfish(字典查询)
题目要求:实现双语词典的单词翻译功能
map实现:
#include <iostream>
#include <map>
#include <sstream>
using namespace std;
map<string, string> dict;
int main() {
string line, en, foreign;
while(getline(cin, line) && !line.empty()) {
istringstream iss(line);
iss >> en >> foreign;
dict[foreign] = en;
}
while(cin >> foreign) {
auto it = dict.find(foreign);
if(it != dict.end())
cout << it->second << endl;
else
cout << "eh" << endl;
}
return 0;
}
例题2:洛谷P3374 【模板】树状数组 1(map模拟解法)
题目要求:实现单点修改、前缀求和操作
map优化解法:
#include <iostream>
#include <map>
using namespace std;
map<int, int> tree; // 离散化存储非零位置
void update(int pos, int val) {
while(pos <= n) {
tree[pos] += val;
pos += pos & -pos;
}
}
int query(int pos) {
int sum = 0;
while(pos > 0) {
sum += tree[pos];
pos -= pos & -pos;
}
return sum;
}
例题3:AtCoder ABC245D - Polynomial division(多项式除法)
题目要求:给定多项式A(x)*B(x)=C(x),已知A和C求B
map存储系数解法:
#include <bits/stdc++.h>
using namespace std;
map<int, int> read_poly(int n) {
map<int, int> res;
for(int i=0; i<=n; ++i) {
int c; cin >> c;
if(c != 0) res[i] = c;
}
return res;
}
int main() {
int n, m;
cin >> n >> m;
auto A = read_poly(n);
auto C = read_poly(n+m);
map<int, int> B;
for(int k=m; k>=0; --k) {
int sum = 0;
for(auto [i,a] : A) {
if(B.find(k-i) != B.end())
sum += a * B[k-i];
}
B[k] = (C[k] - sum) / A.begin()->second;
}
// ...输出B的系数...
return 0;
}
四、进阶应用技巧
1. 离散化坐标压缩
vector<int> coords = {100, 5, 200, 5, 100};
map<int, int> mapping;
int idx = 1;
for(auto x : coords)
if(!mapping.count(x))
mapping[x] = idx++;
2. 时间线管理(扫描线算法)
map<int, int> timeline; // 时间戳 -> 事件数量
void add_event(int t) {
timeline[t]++;
}
void process_events() {
auto it = timeline.begin();
while(it != timeline.end()) {
int t = it->first;
int cnt = it->second;
// 处理t时刻的cnt个事件
it = timeline.erase(it);
}
}
五、练习题推荐
- 洛谷P1276 校门外的树(增强版)(区间状态维护)
- POJ3481 Double Queue(双优先队列模拟)
- AtCoder ABC217D - Cutting Woods(坐标离散化)
- 洛谷P4056 [JSOI2009] 火星藏宝图(动态规划状态优化)
- Codeforces 4C - Registration System(用户名计数)
六、常见错误与优化
典型错误案例
// 错误1:直接访问不存在的键
map<string, int> mp;
cout << mp["unknown"]; // 自动插入默认值0
// 正确做法
if(mp.count("unknown"))
cout << mp["unknown"];
// 错误2:错误删除范围
multimap<int, int> mmp;
mmp.erase(5); // 删除所有key=5的元素
// 正确删除单个元素
auto it = mmp.find(5);
if(it != mmp.end()) mmp.erase(it);
性能优化技巧
-
预分配内存(适用于已知规模):
map<int, int> mp; mp.reserve(1e5); // C++23+支持 -
批量插入优化:
vector<pair<int, int>> data{{1,2},{3,4}}; mp.insert(data.begin(), data.end()); -
利用有序性进行范围统计:
// 统计[key1, key2)区间的value总和 auto it1 = mp.lower_bound(key1); auto it2 = mp.lower_bound(key2); int sum = accumulate(it1, it2, 0, [](int s, auto& p){ return s + p.second; }); -
使用emplace替代insert:
mp.emplace(5, 10); // 避免临时对象构造