C++ 常用STL详解

 一、map

1.1 map原理介绍

  • 基于红黑树(平衡二叉搜索树),红黑树是自平衡二叉搜索树,从根到叶子的最长路径不超过最短路径的2倍,插入删除后通过旋转和重新着色保持平衡。

// 红黑树节点结构
struct RBTreeNode {
    Key key;
    Value value;
    Color color;  // RED or BLACK
    RBTreeNode* left;
    RBTreeNode* right;
    RBTreeNode* parent;
};
  • 比较函数(默认为 std::less<Key>,即升序排列,std::greater<Key>,即降序排列,lambda表达式
#include <map>
#include <functional>  // 需要包含这个头文件
#include <iostream>
#include <string>
using namespace std;

int main(){
    //默认比较器(less),按照键进行升序排序
    map<int,string> defaut_map;
    defaut_map[1] = "one";
    defaut_map[3] = "three";
    defaut_map[2] = "two";
    cout<<"****默认比较器****"<<endl;
    for (const auto& p : defaut_map) {
        cout << p.first << ":" << p.second << " ";  
        // 输出: 3:three 2:two 1:one
    }
    cout<<endl;

    //less比较器,按照键进行升序排序
    map<int,string,less<int>> increasing_map;
    increasing_map[1] = "one";
    increasing_map[3] = "three";
    increasing_map[2] = "two";
    cout<<"****less比较器****"<<endl;
    for (const auto& p : increasing_map) {
        cout << p.first << ":" << p.second << " ";  
        // 输出: 3:three 2:two 1:one
    }
    cout<<endl;

    
    //greater比较器,按照键进行升序排序
    map<int, string, greater<int>> descending_map;
    descending_map[1] = "one";
    descending_map[3] = "three";
    descending_map[2] = "two";
    cout<<"****greater比较器****"<<endl;
    for (const auto& p : descending_map) {
        cout << p.first << ":" << p.second << " ";  
        // 输出: 3:three 2:two 1:one
    }
    cout<<endl;

    // 定义lambda比较器 - 按字符串长度排序
    auto valueLengthComparator = [](const string& a, const string& b) {
        return a.length() < b.length();
    };
    
    // 使用decltype获取lambda类型,并传递lambda给构造函数
    map<string, int, decltype(valueLengthComparator)> lengthMap(valueLengthComparator);
    
    lengthMap["apple"] = 1;
    lengthMap["banana"] = 2;
    lengthMap["cherry"] = 3;
    lengthMap["date"] = 4;
    
    cout << "****按字符串长度排序****:" << endl;
    for (const auto& p : lengthMap) {
        cout << p.first << " (长度:" << p.first.length() << "): " << p.second<<" " ;
    }
    cout<<endl;
}
  • 键的唯一性。提问,在lengthMap的打印过程中会发现,cherry并没有被打印出来,请说明原因?    原因:map中的键具有唯一性,而唯一性是根据比较器来判断的,在lengthMap中比较器为比较字符长度,因此当cherry和banana进行比较时,由于长度一致,所以map将其视为与banana一致,并用cherry的键值3覆盖了banana原本的键值2。

1.2 map的增删改查

先给出总结:

  • 插入使用 emplace() 或 insert()

  • 查找使用 find() 或 contains()(C++20)

  • 修改前先检查键是否存在

  • 注意 operator[] 的创建副作用

  • 删除后不要使用失效的迭代器

操作方法返回值说明
insert()emplace()operator[]插入结果或引用operator[] 最方便但可能意外创建元素
erase(key)erase(iterator)删除数量可按键、迭代器或范围删除
operator[]at(), 迭代器无或引用只能修改值,不能修改键
find()count()contains()迭代器或布尔值find() 最常用,contains() 最直观(C++20)

​​​​​1.2.1 map添加元素

#include <map>
#include <functional>  // 需要包含这个头文件
#include <iostream>
#include <string>
using namespace std;

int main(){
    //增加元素
    cout<<"**** insert ****"<<endl;
    map<int, string> myMap_insert;
    
    // 方法1.1:insert + make_pair
    myMap_insert.insert(make_pair(1, "one"));
    
    // 方法1.2:insert + pair构造函数
    myMap_insert.insert(pair<int, string>(2, "two"));
    
    // 方法1.3:insert + 花括号(C++11)
    myMap_insert.insert({3, "three"});
    
    // 方法1.4:insert + 迭代器提示(性能优化)
    auto insert_hint = myMap_insert.end();
    myMap_insert.insert(insert_hint, {4, "four"});
    
    // 检查插入结果
    auto result = myMap_insert.insert({1, "ONE"}); // 键1已存在,不会插入
    cout << "插入结果: " << (result.second ? "成功" : "失败") << endl;

    for(const auto &it:myMap_insert){
        cout<<it.first<<":"<<it.second<<", ";
    }
    cout<<endl;

    map<int, string> myMap_emplace;
    
    // emplace直接构造元素,避免临时对象
    cout<<"**** emplace ****"<<endl;
    myMap_emplace.emplace(1, "one");           // 最常用
    myMap_emplace.emplace(2, "two");
    myMap_emplace.emplace(3, "three");
    
    // emplace_hint带提示位置
    auto emplace_hint = myMap_emplace.find(2);
    if (emplace_hint != myMap_emplace.end()) {
        myMap_emplace.emplace_hint(emplace_hint, 4, "four");
    }
    
    // 显示结果
    for (const auto& [key, value] : myMap_emplace) {
        cout << key << ":" << value <<", ";
    }
    cout<<endl;

    //使用 operator[]
    cout<<"**** operator[] ****"<<endl;
    map<int, string> myMap;
    // 如果键不存在,会创建并插入默认值
    myMap[1] = "one";
    myMap[2] = "two";
    myMap[3] = "three";
    
    // 注意:operator[] 会创建元素,即使只是读取
    cout <<"myMap[4]: " <<myMap[4] << endl; // 输出空字符串,但创建了键4
    
    // 显示结果
    for (const auto& [key, value] : myMap) {
        cout << key << ":" << value <<", ";
    }
    cout<<endl;

    return 0;
}

1.2.2 map删除元素

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() {
    map<int, string> myMap = {
        {1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}, {5, "five"}
    };
    
    cout << "初始map: ";
    for (const auto& p : myMap) cout << p.first << " ";
    cout << endl;
    
    // 方法2.1:通过键删除
    size_t count = myMap.erase(2); // 返回删除的元素数量(0或1)
    cout << "删除键2,结果: " << count << endl;
    
    // 方法2.2:通过迭代器删除
    auto it = myMap.find(3);
    if (it != myMap.end()) {
        myMap.erase(it); // 删除找到的元素
        cout << "通过迭代器删除键3" << endl;
    }
    
    // 方法2.3:删除一个范围 [first, last)
    auto first = myMap.find(4);
    auto last = myMap.end();
    if (first != myMap.end()) {
        myMap.erase(first, last); // 删除从4到末尾的所有元素
        cout << "删除范围 [4, end)" << endl;
    }
    
    cout << "删除后map: ";
    for (const auto& p : myMap) cout << p.first << " ";
    cout << endl;
    
    return 0;
}

1.2.3 map修改元素

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() {
    map<int, string> myMap = {
        {1, "one"}, {2, "two"}, {3, "three"}
    };
    
    cout << "修改前: ";
    for (const auto& [k, v] : myMap) cout << k << ":" << v << " ";
    cout << endl;
    
    // 方法3.1:使用 operator[](键必须存在)
    myMap[1] = "ONE";           // 直接修改
    myMap[2] = "TWO";
    
    // 方法3.2:使用迭代器
    auto it = myMap.find(3);
    if (it != myMap.end()) {
        it->second = "THREE";   // 修改值
    }
    
    // 方法3.3:使用 at()(C++11,会检查边界)
    try {
        myMap.at(1) = "ONE_MODIFIED";
        // myMap.at(10) = "ten"; // 会抛出std::out_of_range异常
    } catch (const out_of_range& e) {
        cout << "异常: " << e.what() << endl;
    }
    
    cout << "修改后: ";
    for (const auto& [k, v] : myMap) cout << k << ":" << v << " ";
    cout << endl;
    
    return 0;
}

1.2.4 map查询元素

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main() {
    map<int, string> myMap = {
        {1, "one"}, {2, "two"}, {3, "three"}, {4, "four"}
    };
    
    // 方法4.1:使用 find() - 最常用
    auto it = myMap.find(2);
    if (it != myMap.end()) {
        cout << "找到键2: " << it->second << endl;
    } else {
        cout << "未找到键2" << endl;
    }
    
    // 方法4.2:使用 count() - 检查存在性
    if (myMap.count(3) > 0) {
        cout << "键3存在" << endl;
    } else {
        cout << "键3不存在" << endl;
    }
    
    // 方法4.3:使用 contains() - C++20
    #if __cplusplus >= 202002L
    if (myMap.contains(4)) {
        cout << "键4存在" << endl;
    }
    #endif
    
    // 方法4.4:使用 equal_range() - 查找键范围(对multimap更有用)
    auto range = myMap.equal_range(2);
    for (auto it = range.first; it != range.second; ++it) {
        cout << "equal_range找到: " << it->first << ":" << it->second << endl;
    }
    
    return 0;
}

1.2.5 时间复杂度总览

操作平均情况最坏情况说明
插入O(log n)O(log n)红黑树保持平衡
删除O(log n)O(log n)红黑树保持平衡
查找O(log n)O(log n)二分查找特性
修改O(log n)O(log n)需要先查找
遍历O(n)O(n)访问所有元素

二、unordered_map

2.1 unordered_map原理介绍

unordered_map 与 map 最大的不同在于,unordered_map 内部的元素不是排序的,而是通过哈希函数组织到桶(bucket)中,因此其元素的存储顺序是混乱的。unordered_map包含以下特性:

  • 无序性:元素在容器中的顺序并不是按照键的某种顺序(如升序或降序)排列的,而是由哈希函数和键的值决定。遍历 unordered_map 时,你无法得到一个有特定顺序的结果。

  • 唯一键:每个键在 unordered_map 中只能出现一次。

  • 高效的查找性能:基于哈希表实现,其平均时间复杂度为 O(1),即在平均情况下,查找、插入和删除操作都非常快。

  • 内存使用:为了维持高效的查找性能,它可能会消耗比 map 更多的内存。

2.2 unordered_map的增删改查

2.2.1 unordered_map添加元素

#include <iostream>
#include <unordered_map>
#include <string>

using namespace std;

int main() {


    std::unordered_map<std::string, int> u_map;

    // 方法1: 使用 insert 成员函数
    auto result1 = u_map.insert({"key1", 100});
    // result1 是一个 pair<iterator, bool>,第二个元素表示插入是否成功

    // 方法2: 使用 operator[]
    u_map["key2"] = 200;  // 如果 key 不存在则插入,存在则修改

    // 方法3: 使用 emplace (C++11)
    auto result2 = u_map.emplace("key3", 300);

    // 方法4: 使用 insert 和 make_pair
    u_map.insert(std::make_pair("key4", 400));

    // 批量插入
    std::unordered_map<std::string, int> additional = {{"key5", 500}, {"key6", 600}};
    u_map.insert(additional.begin(), additional.end());

    // 显示结果
    for (const auto& [key, value] : u_map) {
        cout << key << ":" << value <<", ";
    }
    cout<<endl;
}

2.2.2 unordered_map删除元素

#include <iostream>
#include <unordered_map>
#include <string>

using namespace std;

int main() {


    // 批量插入
    std::unordered_map<std::string, int> u_map = {{"key1", 100}, {"key2", 200},{"key3", 300}, {"key4",400},{"key5", 500}, {"key6", 600}};


    // 方法1: 通过 key 删除
    size_t count = u_map.erase("key1");  // 返回删除的元素数量(0或1)

    // 方法2: 通过迭代器删除
    auto it = u_map.find("key2");
    if (it != u_map.end()) {
        u_map.erase(it);
    }

    // 方法3: 删除范围内的元素
    auto begin = u_map.begin();
    auto end = u_map.find("key4");
    if (end != u_map.end()) {
        u_map.erase(begin, end);  // 删除 [begin, end) 范围内的元素
    }

    // 显示结果
    for (const auto& [key, value] : u_map) {
        cout << key << ":" << value <<", ";
    }
    cout<<endl;

    // 方法4: 清空所有元素
    u_map.clear();

    // 显示结果
    for (const auto& [key, value] : u_map) {
        cout << key << ":" << value <<", ";
    }
    cout<<endl;
}

2.2.3 unordered_map修改元素

#include <iostream>
#include <unordered_map>
#include <string>

using namespace std;

int main() {


    // 批量插入
    std::unordered_map<std::string, int> u_map = {{"key1", 100}, {"key2", 200},{"key3", 300}, {"key4",400},{"key5", 500}, {"key6", 600}};


    // 方法1: 使用 operator[] (如果key不存在会插入新元素)
    u_map["existing_key"] = 999;

    // 方法2: 使用 at() (如果key不存在会抛出异常)
    try {
        u_map.at("existing_key") = 888;
    } catch (const std::out_of_range& e) {
        std::cout << "Key not found: " << e.what() << std::endl;
    }

    // 方法3: 先查找再修改
    auto it = u_map.find("existing_key");
    if (it != u_map.end()) {
        it->second = 777;  // 修改值
    }

    // 显示结果
    for (const auto& [key, value] : u_map) {
        cout << key << ":" << value <<", ";
    }
    cout<<endl;
}

2.2.4 unordered_map查找元素

#include <iostream>
#include <unordered_map>
#include <string>

using namespace std;

int main() {


    // 批量插入
    std::unordered_map<std::string, int> u_map = {{"key1", 100}, {"key2", 200},{"key3", 300}, {"key4",400},{"key5", 500}, {"key6", 600}};

    // 方法1: 使用 at() (安全,不存在时抛出异常)
    try {
        int value2 = u_map.at("key");
    } catch (const std::out_of_range& e) {
        // 处理key不存在的情况
        cout<<"The key does not exist."<<endl;
    }

    // 方法2: 使用 find()
    auto it = u_map.find("key2");
    if (it != u_map.end()) {
        int value3 = it->second;
        std::cout << "Found: " << value3 << std::endl;
    } else {
        std::cout << "Not found" << std::endl;
    }

    // 方法4: 使用 count() 检查是否存在
    if (u_map.count("key3") > 0) {
        std::cout << "Key exists" << std::endl;
    }

    // 显示结果
    for (const auto& [key, value] : u_map) {
        cout << key << ":" << value <<", ";
    }
    cout<<endl;
}

2.3 unordered_map与map的核心差异对比

特性std::unordered_mapstd::map
底层数据结构哈希表红黑树(平衡二叉搜索树)
元素顺序无序按键的升序排列
时间复杂度平均 O(1),最坏 O(n)稳定 O(log n)
内存占用较高(哈希桶)较低(树节点)
迭代器稳定性插入可能使所有迭代器失效迭代器基本稳定

三、set

与map类似,但存储内容为键而非键值对。

四、unordered_set

与unordered_map类似,但存储内容为键而非键值对。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

10710

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值