一、STL 库基础概念与示例
1. 容器分类
顺序容器
- 核心特性:按元素插入顺序存储,支持下标访问(类似数组),动态扩展内存。
- 典型容器:
vector
(动态数组)。 - 适用场景:需要频繁按顺序访问、尾部插入 / 删除的场景(如队列、栈模拟)。
关联容器
- 核心特性:按元素值的大小自动排序(默认升序),存储位置由值决定,键唯一(
set
/map
的键不可重复)。 - 典型容器:
set
:键值合一(存储的值即键),用于唯一值存储和快速查找。map
:键值对存储(key-value
),键唯一,值可重复。
- 底层结构:二叉搜索树(
set
/map
默认使用红黑树,插入时自动平衡排序)。
2. 常用容器操作对比
操作 | vector(顺序容器) | set(关联容器) | map(关联容器) |
---|---|---|---|
构造 | vector<int> v; | set<int> s; | map<string, int> m; |
新增元素 | v.push_back(10); (尾部插入) | s.insert(10); (自动去重、排序) | m.insert({"apple", 5}); 或 m["apple"] = 5; |
删除元素 | v.erase(it); (需迭代器定位) | s.erase(10); (直接传值删除) | m.erase("apple"); (按键删除) |
修改元素 | v[0] = 20; (下标直接修改) | 不可直接修改键(需 s.erase(old_val); s.insert(new_val); ) | m["apple"] = 10; (直接修改值) |
查询元素 | 需自定义 find 函数(线性查找) | s.find(10) != s.end(); (返回迭代器) | m.find("apple") != m.end(); (按键查找) |
遍历方式 | 下标遍历、迭代器、范围 for | 迭代器、范围 for (不支持下标) | 迭代器、范围 for |
排序 | 需调用 sort(v.begin(), v.end()); | 自动排序(插入时二叉树调整) | 自动按键排序 |
3. 关键注意点
vector
不支持<<
运算符输出- 需手动遍历元素,不能直接
cout << v;
。
- 需手动遍历元素,不能直接
set
的键不可修改- 若需修改键值,必须先删除旧元素,再插入新元素。
map
的键唯一,值可重复- 键用于排序和查找,值可灵活修改(如统计频率)。
二、示例代码
示例 1:vector 存储整数(顺序容器)
cpp
#include <iostream>
#include <vector>
#include <algorithm> // 用于sort
using namespace std;
int main() {
vector<int> nums;
// 插入元素
nums.push_back(3);
nums.push_back(1);
nums.push_back(2);
// 遍历(下标方式)
cout << "插入顺序:";
for (int i = 0; i < nums.size(); i++) {
cout << nums[i] << " "; // 输出:3 1 2
}
// 排序(需手动调用sort)
sort(nums.begin(), nums.end());
cout << "\n排序后:";
for (auto num : nums) { // 范围for遍历
cout << num << " "; // 输出:1 2 3
}
return 0;
}
关键点:vector
按插入顺序存储,需手动排序,支持下标和迭代器遍历。
示例 2:set 统计唯一字符(关联容器)和 去重
cpp
#include <iostream>
#include <set>
using namespace std;
int main() {
string str = "hello";
set<char> unique_chars;
// 插入字符(自动去重,'l' 只存一次)
for (char c : str) {
unique_chars.insert(c);
}
// 遍历(自动排序:'e', 'h', 'l', 'o')
cout << "唯一字符:";
for (char c : unique_chars) {
cout << c << " "; // 输出:e h l o
}
return 0;
}
#include <iostream> // 包含输入输出流头文件,用于cin/cout
#include <set> // 包含set容器头文件,用于有序存储和自动去重
#include <string> // 包含字符串头文件,用于处理输入的字符序列
using namespace std; // 使用标准命名空间,简化代码书写
// 定义Data类,用于存储字符及其出现次数
class Data {
private:
char ch; // 私有成员:存储字符(作为set的键,决定排序和唯一性)
int count; // 私有成员:存储字符出现的次数
public:
// 构造函数:初始化字符和计数,默认计数为1(首次出现)
Data(char ch, int count = 1) : ch(ch), count(count) {}
// 重载<运算符:用于set容器排序(按字符的ASCII值升序)
bool operator<(const Data& r) const {
return ch < r.ch; // 比较字符大小,确保set有序存储
}
// 重载后增量运算符++(int):处理字符重复出现的计数增加
void operator++(int) const {
// 由于函数为const,需通过强制类型转换修改非const成员(注意:此写法不推荐,可能破坏const正确性)
((Data*)this)->count++;
}
// 显示函数:输出字符及其出现次数
void show() const {
cout << ch << "出现了:" << count << "次" << endl;
}
};
int main() {
while (1) { // 无限循环,持续处理用户输入
set<Data> s; // 创建set容器,存储Data对象(自动去重、按ch排序)
string str; // 用于存储用户输入的字符串
cout << "请输入字符:";
cin >> str; // 读取用户输入的一行字符
// 遍历输入字符串的每个字符
for (int i = 0; str[i]; i++) {
// 插入字符到set中,insert返回pair<iterator, bool>:
// first为插入位置迭代器,second表示是否插入成功(true=新插入,false=已存在)
auto p = s.insert(Data(str[i]));
// 如果插入失败(字符已存在),则找到已存在的元素并增加计数
if (!p.second) {
(*p.first)++; // 通过迭代器访问已存在的Data对象,调用后增量运算符
}
}
// 遍历set,输出每个字符的出现次数(set自动按ch排序)
for (auto ele : s) {
ele.show(); // 调用show方法输出结果
}
}
return 0;
}
关键点:set
自动去重并按字符 ASCII 值排序,插入重复元素无效。
示例 3:map 统计单词频率(关联容器)
cpp
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
string sentence = "apple banana apple orange";
map<string, int> word_freq; // key=单词,value=频率
// 统计频率(用operator[]修改值)
for (string word : {"apple", "banana", "apple", "orange"}) {
word_freq[word]++; // 键不存在时自动创建,值初始化为0后+1
}
// 遍历输出(按键自动排序:apple, banana, orange)
cout << "单词频率:" << endl;
for (const auto& pair : word_freq) {
cout << pair.first << ": " << pair.second << endl;
// 输出:
// apple: 2
// banana: 1
// orange: 1
}
return 0;
}
关键点:map
按键自动排序,operator[]
简化值的修改,键唯一(重复插入同一键会更新值)。
三、总结
- 顺序容器(如
vector
)适合 “按顺序操作” 场景,需手动处理排序和查找。 - 关联容器(如
set
/map
)适合 “快速查找、去重、排序” 场景,底层二叉树结构保证高效操作(O(log n)
)。 - 开发中根据需求选择容器:需顺序访问选
vector
,需唯一键选set
,需键值对选map
。