遇到的问题,都有解决方案,希望我的博客能为您提供一点帮助。
一、有序关联容器的迭代器特性
-
迭代器类型
有序关联容器的迭代器是 双向迭代器(Bidirectional Iterator),支持以下操作:-
++it
/it++
:移动到下一个元素(按排序顺序)。 -
--it
/it--
:移动到上一个元素。 -
*it
:访问当前元素的键值对(pair
或元素值)。
-
-
排序顺序
迭代器遍历的顺序是 按关键字升序排列(默认使用<
运算符,可通过自定义比较规则修改)。std::set<int> s = {5, 2, 8, 1}; for (auto it = s.begin(); it != s.end(); ++it) { std::cout << *it << " "; // 输出:1 2 5 8 }
-
修改限制
-
set
/multiset
:迭代器指向的元素值(*it
)不可直接修改(因为会破坏排序)。 -
map
/multimap
:迭代器的first
(键)是const
类型,不可修改;second
(值)可修改。
-
std::map<std::string, int> m = {{"Alice", 30}, {"Bob", 25}};
auto it = m.begin();
it->second = 31; // 正确,修改值
// it->first = "Eve"; // 错误,键是 const
二、迭代器操作
1. 获取迭代器
-
begin()
:返回指向第一个元素的迭代器。 -
end()
:返回指向末尾(最后一个元素的下一个位置)的迭代器。 -
rbegin()
和rend()
:返回反向迭代器,用于逆序遍历。 -
cbegin()
和cend()
:返回常量迭代器(C++11+)。
std::set<int> s = {3, 1, 4};
auto it_begin = s.begin(); // 指向1
auto it_end = s.end(); // 指向末尾后一个位置
2. 遍历容器
vector<int> ivec;
// 正确循环:向 ivec 添加 0到9 两次(原图片代码存在变量名错误)
for (int i = 0; i < 10; ++i) {
ivec.push_back(i);
ivec.push_back(i);
}
set<int> iset(ivec.begin(), ivec.end());
multiset<int> miset(ivec.begin(), ivec.end());
// 遍历 set(输出不重复元素)
cout << "iset: ";
for (auto it = iset.begin(); it != iset.end(); ++it) {
cout << *it << " "; // 输出:0 1 2 3 4 5 6 7 8 9
}
// 遍历 multiset(输出所有元素,包括重复)
cout << "\n miset: ";
for (auto it = miset.begin(); it != miset.end(); ++it) {
cout << *it << " "; // 输出:0 0 1 1 ... 9 9
}
3. 查找元素
find(key)
使用 find(key)
返回指向第一个匹配元素的迭代器,未找到时返回 end()
。
示例(查找 set
中的元素):
auto it = iset.find(5);
if (it != iset.end()) {
cout << "Found: " << *it; // 输出:5
}
lower_bound(key)
和 upper_bound(key)
-
lower_bound(key)
:返回指向第一个不小于key
的元素的迭代器。 -
upper_bound(key)
:返回指向第一个大于key
的元素的迭代器。 -
常用于范围查询(如查找区间
[a, b)
内的元素)。
std::set<int> s = {1, 2, 4, 5, 7};
auto low = s.lower_bound(3); // 指向4
auto high = s.upper_bound(6); // 指向7
for (auto it = low; it != high; ++it) {
std::cout << *it << " "; // 输出:4 5
}
4. 处理重复键(multiset
/multimap
)
对于允许重复关键字的容器(multiset
、multimap
),可以用 equal_range(key)
获取所有匹配元素的范围。
示例(查找 multimap
中所有匹配键的条目):
multimap<string, int> mm;
mm.insert({"apple", 1});
mm.insert({"apple", 2});
mm.insert({"banana", 3});
// 查找所有 "apple" 的条目
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
三、关键操作小结
操作 | 说明 |
---|---|
begin() /end() | 获取指向首元素和尾后元素的迭代器 |
find(key) | 返回指向第一个匹配元素的迭代器(未找到时返回 end() ) |
equal_range(key) | 返回 pair<iterator, iterator> ,表示匹配键的范围(用于重复键) |
lower_bound(key) | 返回第一个不小于 key 的元素的迭代器 |
upper_bound(key) | 返回第一个大于 key 的元素的迭代器 |
四、有序关联容器迭代器失效规则
关联容器的迭代器在以下操作中 可能失效:
-
插入操作:不会使任何迭代器失效(红黑树的结构调整不影响现有迭代器)。
-
删除操作:仅使指向被删除元素的迭代器失效,其他迭代器仍有效。
#include <iostream>
#include <set>
#include<windows.h>
using namespace std;
int main() {
SetConsoleOutputCP(CP_UTF8);//控制台输出中文
set<int> s = {1, 2, 3};
// 获取迭代器
auto it1 = s.find(1);
auto it2 = s.find(2);
auto it3 = s.find(3);
// 验证插入操作
cout << "插入前迭代器指向: " << *it1 << ", " << *it2 << ", " << *it3 << endl;
s.insert(4); // 插入新元素
cout << "插入后迭代器指向: " << *it1 << ", " << *it2 << ", " << *it3 << endl;
// 验证删除操作
auto toErase = s.find(2); // 获取要删除的元素的迭代器
auto nextIt = next(toErase); // 获取下一个元素的迭代器
cout << "删除前: " << *toErase << " 和 " << *nextIt << " 有效" << endl;
s.erase(toErase); // 删除元素2
// 检查被删除元素的迭代器
// cout << *toErase; // 这行会导致未定义行为,注释掉
// 检查其他迭代器
cout << "删除后: " << *nextIt << " 仍然有效" << endl;
return 0;
}
结果输出:
五、综合示例:统计单词频率并遍历结果
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, int> word_count;
word_count["apple"] = 5;
word_count["banana"] = 3;
word_count["cherry"] = 7;
// 遍历并修改值
for (auto it = word_count.begin(); it != word_count.end(); ++it) {
it->second *= 2; // 所有单词频率翻倍
}
// 使用反向迭代器逆序输出
for (auto rit = word_count.rbegin(); rit != word_count.rend(); ++rit) {
std::cout << rit->first << ": " << rit->second << std::endl;
}
return 0;
}
输出:
cherry: 14 banana: 6 apple: 10
五.反向迭代器和正向迭代器的区别
1、遍历方向
特性 | 正向迭代器 | 反向迭代器 |
---|---|---|
遍历方向 | 从头到尾(begin() → end() ) | 从尾到头(rbegin() → rend() ) |
操作符行为 | ++ 指向下一个元素 | ++ 实际指向上一个元素(逻辑反向) |
解引用位置 | 直接指向当前元素 | 指向物理位置的前一个元素 |
#include <iostream>
#include <vector>
#include <windows.h>
using namespace std;
int main() {
SetConsoleOutputCP(CP_UTF8); // 控制台输出中文
vector<int> vec = {1, 2, 3, 4, 5};
// 正向迭代器操作
cout << "正向遍历: ";
for (auto it = vec.begin(); it != vec.end(); ++it) {
cout << *it << " "; // 输出: 1 2 3 4 5
}
cout << endl;
// 反向迭代器操作
cout << "反向遍历: ";
for (auto rit = vec.rbegin(); rit != vec.rend(); rit++) {
cout << *rit << " "; // 输出: 5 4 3 2 1
}
cout << endl;
// 验证迭代器位置关系
auto mid = vec.begin() + 2; // 指向3
cout << "中间元素: " << *mid << endl;
auto rmid = reverse_iterator<vector<int>::iterator>(vec.rbegin()+2); // 反向迭代器指向3
cout << "反向迭代器对应元素: " << *rmid << endl;
// 验证base()方法
cout << "反向迭代器base()指向: " << *rmid.base() << endl; // 输出4
return 0;
}
输出结果:
如果还有其他的问题,欢迎评论区留言,期待与您的智慧碰撞