【C++STL】unordered_map学习

简介

unordered_map 是 C++ STL(标准模板库)中提供的一种 哈希表 关联式容器,它可以存储键值对key-value,并且提供了快速的查找、插入和删除操作。
map 不同,unordered_map 中的元素并不会按照键的顺序排列,而是根据元素的哈希值来存储和查找,因此它的插入和查找操作通常比 map 更高效,时间复杂度接近于 O(1),允许你在平均常数时间内进行插入、删除和查找操作。它不保证内部元素的顺序,同时key唯一,对应value,二者数据类型可以不同。

定义

unordered_map 是 C++11 标准引入的,定义在 <unordered_map> 头文件中。

#include <unordered_map>
using namespace std;

unordered_map<Key, T>

//Key:键的类型。
//T:值的类型。

unordered_map<type,type> name;
unordered_map<string, int> um1; // 构造一个key为string类型,value为int类型的 空容器

unordered_map<string, int> um1({ {"apple", 1}, {"lemon", 2}});
unordered_map<string, int> um2(um1); // 拷贝构造同类型容器um1的复制品

unordered_map<string, int> um1({ {"apple", 1}, {"lemon", 2}});
unordered_map<string, int> um3(um1.begin(), um1.end()); // 使用迭代器拷贝构造um1容器某段区间的复制品

基本特性

  1. 哈希表实现:内部使用哈希表实现,每个键值对通过键值的哈希值分配到不同的桶(bucket)中,因此查找效率高,通常是 O(1)。

  2. 无序存储unordered_map 中的元素顺序是无序的,因为它们的排列依赖于哈希值,而不是键的大小或插入顺序。在 unordered_map 中,元素的存储顺序是 无序的,这意味着它们并不是按照插入的顺序存储的。unordered_map 使用哈希表(hash table)来存储键值对,并且基于哈希值进行分组(即“桶”)。因此,元素的遍历顺序并不保证与插入顺序一致,也不一定与键的大小相关

  3. 不允许重复键unordered_map 不允许有相同的键,每个键只能对应一个值。如果插入一个已经存在的键,则新值会覆盖旧值。

  4. 自动扩容:当存储的元素数量超过负载因子(load factor)时,unordered_map 会自动扩容,重新分配哈希表。

示例程序

#include <iostream>
#include <unordered_map>
using namespace std;

int main() {
    // 创建一个 unordered_map
    unordered_map<int, string> umap;

    // 插入元素,通过[]直接访问,umap[key]
    umap[1] = "apple";    // 插入键值对 (1, "apple")
    umap[2] = "banana";   // 插入键值对 (2, "banana")
    umap[3] = "cherry";   // 插入键值对 (3, "cherry")

    // 访问元素
    cout << "Key 1: " << umap[1] << endl;  // 通过键访问对应的值
    cout << "Key 2: " << umap[2] << endl;
    
    // 检查键是否存在
    if (umap.find(3) != umap.end()) {
        cout << "Key 3 exists in the map" << endl;
    }

    // 遍历 unordered_map
    for (auto& pair : umap) {
        cout << "Key: " << pair.first << ", Value: " << pair.second << endl;
    }

    return 0;
}

/*
输出:
Key 1: apple
Key 2: banana
Key 3 exists in the map
Key: 1, Value: apple
Key: 2, Value: banana
Key: 3, Value: cherry
*/

基本操作

插入元素

// 创建一个 unordered_map
unordered_map<int, string> umap;

umap[key] = value;  
// 或者
umap.insert({key, value});//小括号与花括号,键值对用花括号

insert() 的返回值是一个 pair<iterator, bool>:

iterator:指向 插入的位置(无论是新插入的元素,还是原本已经存在的元素)。
bool:如果元素被成功插入,返回 true;如果该键已经存在,则返回 false。
/*   
在 unordered_map 中,insert() 方法是用来 插入新的键值对 的。如果你尝试插入一个 已经存在的键,那么 insert() 不会进行任何修改,且返回一个指向原始元素的迭代器,并且 insert() 方法会返回一个 pair<iterator, bool>。
*/

//示例
#include <iostream>
#include <unordered_map>
using namespace std;

int main() {
    unordered_map<int, string> mp = {{1, "word"}};

    // 尝试插入已存在的键值对
    auto result = mp.insert({1, "new_word"});
    
    if (result.second) {
        cout << "Inserted new element." << endl;
    } else {
        cout << "Key already exists. Value not updated." << endl;
    }

    // 输出 unordered_map 中的元素
    for (const auto& pair : mp) {
        cout << pair.first << ": " << pair.second << endl;
    }

    return 0;
}
输出:
Key already exists. Value not updated.
1: word
/*
mp.insert({1, "new_word"}); 尝试插入键为 1 的元素,但是键 1 已经存在,且值为 "word"。
insert() 返回的 result.second 是 false,说明键 1 已经存在,所以 unordered_map 没有插入新的元素。
最终,unordered_map 中的元素没有变化,依旧是 {1: "word"}。
*/
    
如果你希望修改 已存在的元素,可以使用 operator[] 或 find() 来访问并修改值:

1. 使用 operator[] 修改
mp[1] = "new_word";  // 修改键 1 对应的值
 
2. 使用 find() 修改
auto it = mp.find(1);
if (it != mp.end()) {
    it->second = "new_word";  // 修改找到的元素的值
}

查找元素

使用 find() 方法查找元素。find() 返回一个迭代器,指向查找到的元素。如果没有找到,返回 end()

auto it = umap.find(key);// 使用 auto 简化迭代器类型的声明

if (it != umap.end()) {
    // 找到元素 it->first 表示键,it->second 表示值
}
/*
auto 推导类型
it 是迭代器,而 umap.find(key) 返回的是一个指向 unordered_map 中 pair<int, string> 类型元素的迭代器。
auto 自动推导出 it 的类型为 unordered_map<int, string>::iterator,这避免了显式声明迭代器类型。
*/

插入链接待

访问元素

使用 operator[] 可以访问或修改元素的值。如果键不存在,operator[] 会插入一个默认值。

cout << umap[key];  // 获取值
umap[key] = value;   // 更新值

删除元素

使用 erase() 方法删除一个指定的元素,支持通过键或迭代器删除。

umap.erase(key);  // 删除指定键的元素
umap.erase(it);   // 删除指定迭代器指向的元素

检查是否包含某个键

  • 使用 count()find() 来检查 unordered_map 是否包含某个键。

  • count()

    • count() 是专门用来检查某个键是否存在的,它的返回值要么是 0(键不存在),要么是 1(键存在)。

    • 它不返回关于元素的其他信息,只告诉你是否存在这个键。

    • 适用于只关心键是否存在的场景。

    • if (umap.count(key) > 0) {
          // 键存在
      }

    find()

    • find() 返回一个迭代器。如果找到了对应的键,返回一个指向该键值对的迭代器;如果找不到,返回 end() 迭代器。

    • 它除了告诉你键是否存在外,还可以用来获取该键对应的值(通过迭代器访问)。

    • find() 更灵活,适用于你需要键存在并同时想访问对应值的场景。

    • #include <iostream>
      #include <unordered_map>
      using namespace std;
      
      int main() {
          // 创建 unordered_map,初始化一些键值对
          unordered_map<int, string> umap = {{1, "apple"}, {2, "banana"}, {3, "cherry"}};
      
          // 使用 find() 检查键 2 是否存在
          auto it = umap.find(2);  // 查找键为 2 的元素
      
          // 如果找到键 2,it 将指向该键的元素
          if (it != umap.end()) {
              cout << "Key 2 exists. Value: " << it->second << endl;
       //it->second 访问的是 unordered_map 中 it 指向的元素的值。it->first 是键,it->second 是值。
          } else {
              // 键 2 不存在
              cout << "Key 2 does not exist." << endl;
          }
          return 0;
      }
      

      大小和清空

    • size() 返回容器中元素的个数。

    • empty() 检查容器是否为空。

    • clear() 清空容器的所有元素。

umap.size();     // 返回元素个数
umap.empty();    // 返回 true 如果容器为空
umap.clear();    // 清空容器

遍历

unordered_map 提供多种遍历方式,以下是常用的几种方法:

    1. 使用范围基于范围的 for 循环(C++11及以上)

    这种方式简单且常用:

    #include <iostream>
    #include <unordered_map>
    using namespace std;

    int main() {
        unordered_map<int, string> mp = {{1, "one"}, {2, "two"}, {3, "three"}};

        for (const auto& pair : mp) {
            cout << pair.first << ": " << pair.second << endl;
        }

        return 0;
    }

    输出示例:
    1: one
    3: three
    2: two
    
    特点:
    遍历顺序无序(依赖于哈希表的存储顺序)。
    使用 `auto&` 可以避免拷贝,直接引用元素。

    2. 使用迭代器

    unordered_map 提供 begin() 和 end() 方法,可以用迭代器遍历元素。

    #include <iostream>
    #include <unordered_map>
    using namespace std;

    int main() {
        unordered_map<int, string> mp = {{1, "one"}, {2, "two"}, {3, "three"}};

        for (auto it = mp.begin(); it != mp.end(); ++it) {
            cout << it->first << ": " << it->second << endl;
        }

        return 0;
    }
    
    输出示例:
    1: one
    3: three
    2: two
    

    特点:
    it->first 访问键,it->second 访问值。
    适合需要灵活控制遍历逻辑的场景。

    3. 遍历键或值

    如果只需要访问键或值,可以在遍历中只提取所需部分。

    遍历键:
    for (const auto& pair : mp) {
        cout << "Key: " << pair.first << endl;
    }

    遍历值:
    for (const auto& pair : mp) {
        cout << "Value: " << pair.second << endl;
    }

    4. 使用 for_each 和 Lambda 表达式

    通过标准库算法 for_each 结合 Lambda 表达式来遍历 unordered_map:

    #include <iostream>
    #include <unordered_map>
    #include <algorithm>
    using namespace std;

    int main() {
        unordered_map<int, string> mp = {{1, "one"}, {2, "two"}, {3, "three"}};

        for_each(mp.begin(), mp.end(), [](const pair<int, string>& pair) {
            cout << pair.first << ": " << pair.second << endl;
        });

        return 0;
    }

    5. 按条件筛选遍历

    可以在遍历时添加条件筛选:
    int main() {
        unordered_map<int, string> mp = {{1, "one"}, {2, "two"}, {3, "three"}};

        for (const auto& pair : mp) {
            if (pair.first % 2 == 0) {  // 只打印键为偶数的键值对
                cout << pair.first << ": " << pair.second << endl;
            }
        }

        return 0;
    }

    // 小结

    简单遍历:使用范围 for 循环最方便。
    灵活操作:使用迭代器可以精确控制遍历行为。
    条件筛选:自定义条件遍历键值对。
    注意: 遍历顺序是无序的,与插入顺序无关。如果需要有序遍历,请使用 map。

时间复杂度

  • 查找、插入、删除:一般情况下,它们的时间复杂度为 O(1),但在极端情况下(例如哈希冲突严重时),可能会退化到 O(n)。

  • 遍历unordered_map 中的元素是无序存储的,因此遍历的时间复杂度为 O(n),其中 n 是容器中元素的数量。

比较 unordered_mapmap

特性unordered_mapmap
存储结构哈希表(无序)平衡二叉树(有序)
查找时间复杂度O(1)(均摊)O(log n)
元素顺序无序按照键值升序排序
插入时间复杂度O(1)(均摊)O(log n)
删除时间复杂度O(1)(均摊)O(log n)

总结

unordered_map 是 C++ STL 中的一个非常有用的容器,适用于需要通过键进行快速查找、插入和删除操作的场景。它内部使用哈希表实现,提供了 O(1) 时间复杂度的查找和插入操作。
使用 unordered_map 时需要注意,它是 无序 的,因此不能依赖元素的插入顺序或排序顺序。如果需要有序存储,应该选择 map。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值