C++:有序关联容器的遍历和查找

遇到的问题,都有解决方案,希望我的博客能为您提供一点帮助。

一、有序关联容器的迭代器特性

  1. 迭代器类型
    有序关联容器的迭代器是 ​双向迭代器(Bidirectional Iterator),支持以下操作:

    • ++it / it++:移动到下一个元素(按排序顺序)。

    • --it / it--:移动到上一个元素。

    • *it:访问当前元素的键值对(pair 或元素值)。

  2. 排序顺序
    迭代器遍历的顺序是 ​按关键字升序排列​(默认使用 < 运算符,可通过自定义比较规则修改)。

    std::set<int> s = {5, 2, 8, 1};
    for (auto it = s.begin(); it != s.end(); ++it) {
        std::cout << *it << " "; // 输出:1 2 5 8
    }
  3. 修改限制

    • 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)​

对于允许重复关键字的容器(multisetmultimap),可以用 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;
}

输出结果:

 如果还有其他的问题,欢迎评论区留言,期待与您的智慧碰撞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚戏师

多谢道友

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值