一、笔记前言
本篇博客所有知识点、核心注释、案例代码均来自本人手写实战代码,原汁原味保留学习笔记逻辑。
内容覆盖:序列容器(deque、list)、有序关联容器(map、multimap、set)、无序关联容器(unordered_map)。的结构,适合复习、期末、面试复盘。
二、序列容器知识点回顾(deque / list)
1. deque 双端队列
核心笔记
-
底层结构:多段连续空间(并非一整块连续内存)
-
核心操作:双端增删
-
尾部:
push_back、emplace_back、pop_back -
头部:
push_front、emplace_front、pop_front
-
-
元素访问 :支持
[]、at()、front()、back() -
通用接口 :
size()、empty()、erase()、clear() -
迭代器特性 :支持
++、--、>、<、!=、==(随机访问迭代器) -
时间复杂度:头尾插入 O(1),中间插入/删除 O(n),查找 O(n)
2. list 双向链表
核心笔记
-
底层结构:双向链表,内存空间不连续
-
接口特点:大部分函数和 deque 一致
-
重要限制(易错点) :不支持 \[\]、at() 下标访问
-
迭代器特性 :仅支持
++、--、!=、==,不支持大小比较、不支持随机访问 -
时间复杂度:任意位置插入、删除 O(1),查找 O(n)
3. deque 与 list 效率对比总结
-
插入:deque/list 尾部 O(1);deque 中间插入 O(n)
-
删除:deque O(n);list O(1)
-
查找:deque、list 均为 O(n)
三、有序关联容器:map / multimap
1. map 核心原理
核心笔记
-
底层结构:红黑树(自平衡二叉搜索树)
-
时间复杂度:增删改查均为 O(logN)
-
特性 :按键 Key 自动排序,Key 唯一不可重复
-
存储单元 :
pair<K, V>键值对-
pair.first:键 K -
pair.second:值 V
-
-
插入规则:
-
map[K] = V:K 不存在则插入,K 存在则覆盖更新值 -
emplace() / insert():插入键值对
-
-
常用接口 :
at(K)、erase(K)、count(K)、find(K)、equal_range(K) -
迭代器 :支持
++、--、!=、==
2. multimap(多键映射容器)
核心笔记
-
特性 :按键排序,Key 允许重复(一个键对应多个值)
-
硬性限制(高频易错) :不支持 \[\]、at() 访问
-
适用场景:一对多关系
-
班级号 --- 多名学生
-
教师编号 --- 多门授课课程
-
-
批量查询方案:
-
find(K):返回第一个匹配 Key 的迭代器,后续遍历判断 -
equal_range(K):直接返回当前 Key 的所有元素迭代器区间
-
-
删除规则 :
erase(Key):删除当前 Key 对应的所有元素 -
边界查找:
-
lower_bound(K):找 >= K 的第一个元素 -
upper_bound(K):找 > K 的第一个元素
-
对应完整代码片段
cpp
#include <iostream>
#include <map>
#include <string>
using namespace std;
template<class K, class V>
void show(multimap<K, V>& data) {
auto it = data.begin();
while (it != data.end()) {
cout << it->first << ": " << it->second << " ";
it++;
}
cout << endl;
}
int main() {
// 教师编号 - 授课课程 一对多映射
multimap<int, string> tCourses{
{11, "C++"}, {12, "Linux"},
{11, "Qt"}, {14, "C"} };
// 不支持[]赋值,仅可使用 emplace / insert
tCourses.emplace(make_pair(10, "MySQL"));
tCourses.insert({
{9, "网络编程"},
{11, "数据库编程"}
});
show(tCourses);
// 方式1:find 遍历查询相同键的所有值
auto cit = tCourses.find(11);
while (cit != tCourses.end()) {
if (cit->first != 11) break;
cout << cit->second << " ";
cit++;
}
cout << endl;
// 方式2:equal_range 获取同键所有元素范围
auto range = tCourses.equal_range(11);
for (auto it = range.first; it != range.second; ++it) {
cout << "Value: " << it->second << endl;
}
// 删除所有键为11的元素
cout << "删除11编号老师的所有课程" << endl;
tCourses.erase(11);
show(tCourses);
// 修改指定键对应的值
auto it = tCourses.begin();
while (it != tCourses.end()) {
if (it->first == 12) {
it->second = "Linux C";
break;
}
it++;
}
show(tCourses);
// 上下边界查找
auto ret1 = tCourses.upper_bound(12); // > 12
cout << "大于12的首个键:" << ret1->first << endl;
auto ret2 = tCourses.lower_bound(11); // >= 11
cout << "大于等于11的首个键:" << ret2->first << endl;
return 0;
}
3. 自定义类作为 map/multimap 的 Key
核心笔记
-
map/multimap 底层红黑树需要比较键的大小来排序
-
自定义类作为 Key 时,必须重载 < 运算符
-
重载函数必须加双重 const(参数const + 函数const)
对应完整代码片段
cpp
#include <iostream>
#include <map>
#include <string>
using namespace std;
// 班级类:作为multimap的键
class Cls {
public:
Cls(int id, string name) : cid(id), name(name) {}
// 必须重载 < 运算符,双重const不可省略
bool operator<(const Cls& c) const {
return cid < c.cid;
}
// 重载输出
friend ostream& operator<<(ostream& cout,const Cls& c) {
cout<< "id: " << c.cid << ", name: " << c.name;
return cout;
}
private:
int cid;
string name;
};
// 学生类:作为value
class Stu {
public:
Stu(int id, string name) : sid(id), name(name) {}
friend ostream& operator<<(ostream& cout,const Stu& c) {
cout << "id: " << c.sid << ", name: " << c.name;
return cout;
}
private:
int sid;
string name;
};
int main() {
// 班级 - 学生 一对多映射
multimap<Cls, Stu> datas;
Cls c1(1001, "C++2501"), c2(1005, "Linux C");
datas.emplace(c1, Stu(1, "Lucy"));
datas.emplace(c1, Stu(2, "Tom"));
datas.emplace(c2, Stu(3, "Jerry"));
datas.emplace(c2, Stu(4, "Jack"));
// 遍历所有班级学生
auto it = datas.begin();
while (it != datas.end()) {
cout<< it->first << " : " << it->second << endl;
it++;
}
// 统计当前班级的学生数量
size_t len= datas.count(c1);
cout<< "C++2501班级人数: " << len << endl;
// 删除该班级所有学生
datas.erase(c1);
len = datas.count(c1);
cout << "删除后班级人数: " << len << endl;
return 0;
}
四、set 容器(自动排序 + 自动去重)
1. set 核心知识点
核心笔记
-
底层结构:同 map,红黑树
-
存储特点:只有 Key,没有 Value
-
核心能力 :自动去重 + 自动升序排序
-
常用场景:数据去重、有序筛选
-
常用接口 :
emplace、erase、find、count、lower_bound、upper_bound
基础使用代码
cpp
#include<iostream>
#include<set>
#include<vector>
using namespace std;
int main() {
vector<int> n1({ 1,9,7,2,1,4,3,5,7,9,2,3 });
cout<<"n1 size: "<<n1.size()<<endl;
// 利用set自动去重+排序
set<int> s1(n1.begin(), n1.end());
set<int>::const_iterator it = s1.cbegin();
cout<<"s1 size: "<<s1.size()<<endl;
while (it != s1.cend()) {
cout << *it << " ";
it++;
}
cout << endl;
// 插入、删除元素
s1.emplace(21);
s1.erase(7);
cout << "s1 size: " << s1.size() << endl;
it= s1.cbegin();
while (it != s1.cend()) {
cout << *it << " ";
it++;
}
cout << endl;
// 元素查找
cout << s1.count(2) << endl;
cout<<(s1.count(9)>0?*s1.find(9):0)<<endl;
return 0;
}
2. 实战:vector 去重并保留原始顺序
核心笔记
-
原生 set 去重会打乱原有顺序
-
解决方案:用 set 记录已出现元素,遍历 vector 保留首次出现元素
-
最终实现:去重 + 保留原有相对位置
对应代码
cpp
// vector去重,保留原有元素顺序
int main() {
vector<int> nums({ 1,9,7,2,1,4,3,5,7,9,2,3 });
vector<int> ret;
ret.reserve(nums.size());
set<int> viewed; // 记录已经出现过的元素
auto it = nums.begin();
while (it != nums.end()) {
// 当前元素未出现过,才存入结果
if (viewed.count(*it) == 0) {
viewed.emplace(*it);
ret.emplace_back(*it);
}
it++;
}
// 交换容器,nums存储去重结果
nums.swap(ret);
it = nums.begin();
while (it != nums.end()) {
cout << *it << " ";
it++;
}
cout << endl;
return 0;
}
五、无序关联容器:unordered_map(哈希表)
1. 核心知识点 & 面试考点
核心笔记
-
底层结构:哈希表(vector桶 + 链表 + 哈希函数,链地址法)
-
性能特点:查找速度极快,平均时间复杂度 O(1)
-
存储特点 :无序存储、键唯一、不自动排序
-
map 与 unordered_map 面试区别
-
map:红黑树、有序、O(logN)、稳定、内存紧凑
-
unordered_map:哈希表、无序、平均O(1)、查找快、有哈希冲突
-
对应代码
cpp
#include<iostream>
#include<unordered_map>
#include<string>
using namespace std;
int main() {
unordered_map<int, string> m1;
m1[1001] = "张三";
m1[1009] = "李四";
m1[1002] = "王六";
m1[1008] = "王晓六";
m1.emplace(1003, "张三");
// 遍历:无序输出
auto it = m1.begin();
while (it != m1.end()) {
cout << it->first << " : " << it->second << endl;
it++;
}
// 查找与判断
cout << m1.count(1001) << endl;
cout << m1.count(1004) << endl;
cout << (m1.count(1009) > 0 ? m1.find(1009)->second : "no") << endl;
cout << (m1.count(1004) > 0 ? m1.find(1004)->second : "no") << endl;
// 删除元素
m1.erase(1001);
cout << m1.count(1001) << endl;
it = m1.begin();
while (it != m1.end()) {
cout << it->first << " : " << it->second << endl;
it++;
}
return 0;
}
六、全文核心总结(面试必背)
-
序列容器:deque 适合双端增删;list 适合频繁任意位置增删
-
map:红黑树、有序、键唯一、O(logN)
-
multimap :红黑树、有序、键可重复、无\[\]访问
-
自定义类做键 :必须重载
<运算符 + 双const -
set:红黑树、自动排序去重
-
unordered_map:哈希表、无序、查找最快、日常高频使用