当前位置: 首页 > news >正文

【C++】unordered_map、unordered_set 的使用

文章目录

  • 1. `unordered_set` 和 `unoredered_map` 的概念
    • 1.1 基本概念
    • 1.2 与 map、set 的对比
  • 2. `unordered_set` 和 `unoredered_map` 的常见使用
    • 2.1 构造函数
      • 2.1.1 `unordered_set` 的构造函数
      • 2.1.2 `unordered_map` 的构造函数
    • 2.2 常用接口
      • 2.2.1 插入操作
        • 2.2.1.1 insert 函数
        • 2.2.1.2 emplace 函数
      • 2.2.2 查找
        • 2.2.2.1 find 函数
        • 2.2.2.2 count 函数
      • 2.2.3 operator[]
      • 2.2.4 删除操作
        • 2.2.4.1 erase 删除元素
        • 2.2.4.2 clear 函数
    • 2.3 C++ 手册中的 `unordered_map`、`unordered_set`
      • 2.3.1 自定义哈希函数
      • 2.3.2 自定义比较函数

1. unordered_setunoredered_map 的概念

unordered_mapunordered_set 是 C++ 标准库中基于哈希表实现的容器,适用于高效查找、插入和删除操作,不保证元素顺序。

1.1 基本概念

  • unordered_map
    • 存储键值对(key-value),键唯一,值可重复
    • 通过哈希函数将键映射到桶(bucket),支持快速访问值(平均 O(1) 时间复杂度)。
    • 适用场景:统计频率、缓存键值数据等(如统计单词出现次数)。
  • unordered_set
    • 仅存储唯一键的集合,不允许重复元素
    • 同样基于哈希表,用于快速检查元素是否存在(如过滤重复元素)。

1.2 与 map、set 的对比

特性unordered_map/unordered_setmap/set
底层实现哈希表红黑树
元素顺序无序按键排序
查找时间复杂度平均O(1),最坏O(n)O(logN)
插入/删除时间复杂度平均O(1),最坏O(n)O(logN)
内存开销较高(需要维护桶结构)较低
是否需要哈希函数是(需要为键定义哈希函数)否(需要重载比较运算符)

2. unordered_setunoredered_map 的常见使用

2.1 构造函数

2.1.1 unordered_set 的构造函数

构造函数接口说明
unordered_set()构造一个空的 unordered_set
unordered_set(InputIterator first, InputIterator last)使用 [first, last) 区间的元素初始化容器。
unordered_set(const unordered_set& us)拷贝构造,生成与 us 相同的容器。
unordered_set(std::initializer_list<value_type> il)使用初始化列表构造容器。

使用示例:

#include <iostream>
using namespace std;
#include <unordered_set>
#include <vector>int main()
{unordered_set<int> s1; // 构造空的容器vector<int> v = { 7,3,6,2,8,9 };unordered_set<int> s2(v.begin(), v.end()); // 迭代器区间构造unordered_set<int> s3 = s2; // 拷贝构造,也可写成 s3(s2)unordered_set<int> s4 = { 2,6,9,4,7,1 }; // 初始化列表构造return 0;
}

2.1.2 unordered_map 的构造函数

构造函数接口说明
unordered_map()构造一个空的 unordered_map
unordered_map(InputIterator first, InputIterator last)使用 [first, last) 区间的元素初始化容器。
unordered_map(const unordered_map& um)拷贝构造,生成与 us 相同的容器。
unordered_map(std::initializer_list<value_type> il)使用初始化列表构造容器。

使用示例:

#include <iostream>
using namespace std;
#include <unordered_map>
#include <vector>int main()
{unordered_map<string, string> m1; // 构造空的容器vector<pair<string, string>> v = { {"left", "左"}, {"right", "右"},{"hello", "你好"}, {"zkp", "ZKP"} };unordered_map<string, string> m2(v.begin(), v.end()); // 迭代器区间构造unordered_map<string, string> m3 = m2; // 拷贝构造,也可写成 m3(m2)unordered_map<string, string> m4 = { {"left", "左"}, {"right", "右"},{"hello", "你好"}, {"zkp", "ZKP"} }; // 初始化列表构造return 0;
}

2.2 常用接口

2.2.1 插入操作

2.2.1.1 insert 函数

返回值:
pair<iterator, bool>

时间复杂度:O(1),哈希冲突过多可能会导致退化为 O(N)

适用场景:

  • 插入单个元素或范围元素
  • 避免覆盖已存在的键(unordered_map)或元素(unordered_set)

unordered_map

// unordered_map
int main()
{unordered_map<string, int> m;m.insert({ "left", 1 });  // 插入元素m.insert({ "right", 2 }); // 正常插入m.insert({ "left", 3 });  // 再插入和前面相同键的元素会导致插入失败auto it = m.begin();while (it != m.end()){cout << it->first << ": " << it->second << endl;++it;}return 0;
}

在这里插入图片描述

unordered_set

// unordered_set
int main()
{unordered_set<int> s;s.insert(1);  // 插入元素s.insert(2); // 正常插入pair<unordered_set<int>::iterator, bool> flag = s.insert(1);  // 再插入和前面相同键的元素会导致插入失败// auto result = s.insert(1); 也可以这么写if (flag.second)cout << "insert sucess" << endl;elsecout << "insert fail" << endl;return 0;
}

在这里插入图片描述

2.2.1.2 emplace 函数

emplaceunordered_mapunordered_set 等容器提供的高效插入方法,它允许直接在容器内部构造元素,避免临时对象的创建和拷贝/移动开销。

用法、返回值什么的其实和 insert 基本上一样,用来提高插入效率的。
但是,实际上的用法还是有一点点区别,主要体现在初始化列表上。
例如,当你尝试 emplace({"left, 1})时:

  • {"left", 1} 是一个 初始化列表(std::initializer_list),它会被编译器视为一个单一参数
  • emplace 的模板参数推导需要将参数分解为 键和值的独立构造参数,而初始化列表无法被分解为多个独立参数

对比 insert
为什么 insert 可以使用 insert({"left", 1}),而 emplace 不能呢?
下面来说说我自己的理解:

insert 支持 insert({“left”, 1}),是因为它的参数是 一个已经构造好的元素
这句话的意思是,insert 它支持是因为它已经构造好了一个元素,可以直接使用参数进行初始化
emplace参数仅仅只是参数,需要使用参数进行构造并初始化,所以emplace 无法推导列表中的参数到底是什么类型
也就是说,emplace直接在容器里面进行构造,然后再通过我们传递的参数对构造出来的元素进行初始化

unordered_map

int main()
{unordered_map<string, int> m;//m.emplace({ "left", 1 });  // 这样是不行的m.emplace("left", 1);  // 插入元素m.emplace("right", 2 ); // 正常插入m.emplace("left", 3 );  // 再插入和前面相同键的元素会导致插入失败auto it = m.begin();while (it != m.end()){cout << it->first << ": " << it->second << endl;++it;}return 0;
}

在这里插入图片描述

unordered_set

int main()
{unordered_set<int> s;s.emplace(1);  // 插入元素s.emplace(2); // 正常插入pair<unordered_set<int>::iterator, bool> flag = s.emplace(1);  // 再插入和前面相同键的元素会导致插入失败// auto result = s.emplace(1); 也可以这么写if (flag.second)cout << "insert sucess" << endl;elsecout << "insert fail" << endl;return 0;
}

在这里插入图片描述

2.2.2 查找

2.2.2.1 find 函数
iterator find(const Key& key);             // 非 const 版本
const_iterator find(const Key& key) const; // const 版本

参数

  • key:要查找的键(Key 类型)。

返回值

  • 如果找到键,返回指向该键值对的 迭代器(iterator 或 const_iterator)。
  • 如果未找到键,返回 end() 迭代器。

哈希表的查找接口比较简单,直接看代码。

unordered_map

int main()
{unordered_map<string, int> m;m.emplace("left", 1); m.emplace("right", 2 ); m.emplace("zkp", 3 );  auto result = m.find("zkp");if (result != m.end())cout << "Found: " << result->second << endl;elsecout << "Not Found" << endl;return 0;
}

在这里插入图片描述

unordered_set

int main()
{unordered_set<int> s;s.emplace(1);  s.emplace(2); auto result = s.find(3);if (result != s.end())cout << "Found: " << *result << endl;elsecout << "Not Found" << endl;return 0;
}

在这里插入图片描述

2.2.2.2 count 函数

count 函数看名字就知道是统计某一个键值出现的次数,但是 unordered_mapunordered_set 都是不允许重复键值的容器,所以它的值只会是 0 或 1。我们平常在unordered_mapunordered_set中使用的查找更多是 findcount函数一般是在允许重复键值的容器中使用,如:unordered_multimapunordered_multiset

// unordered_multiset
int main()
{unordered_multiset<int> ms;ms.insert(1);ms.insert(1);ms.insert(1);cout << ms.count(1) << endl;return 0;
}

在这里插入图片描述

2.2.3 operator[]

这个是 unordered_map 的特性,unordered_set仅存储唯一元素,只有,没有的概念,因此不需要 operator[]

我们之前在讲 map 容器的时候说过,map 容器允许直接使用 operator[] 进行插入、查找、修改的行为,这个 unordered_map 容器一样允许插入、查找、修改。

操作行为适用场景
插入键不存在时自动插入,值初始化快速插入键值对
修改键存在时直接修改值更新已有的键
查找键存在时返回值,不存在插入时有副作用需谨慎!查找还是建议用find

因为只要键值不存在,你通过operator[]查找 Key 的时候,是会插入的,所以还是建议使用 find

int main()
{unordered_map<string, int> m;m["zkp"]++; // 键不存在,插入cout << m["zkp"] << endl;  // 查找m["zkp"]++; // 键存在,修改值cout << m["zkp"] << endl;m["zkp"] = 3; // 键存在,修改值cout << m["zkp"] << endl;if (!m["ZKP"]) // 因为 "ZKP" 这个键并不存在,这里会直接插入 "ZKP",并将它的值初始化为 0cout << m["ZKP"] << endl;return 0;
}

在这里插入图片描述

2.2.4 删除操作

2.2.4.1 erase 删除元素

erase 常用下面几种

函数名功能返回值
iterator erase(iterator pos)删除迭代器 pos 指向的元素返回下一个有效迭代器
size_type erase(const Key& key)删除键为 Key 的元素返回删除的元素个数

unordered_map

int main()
{unordered_map<string, int> m;m["zkp"] = 1;m["Zkp"] = 2;m["ZKp"] = 3;m["ZKP"] = 4;m.erase("zkp"); // 删除键为 "zkp" 的元素auto pos = m.find("Zkp");m.erase(pos); // 删除迭代器为 pos 的元素for (auto& it : m)cout << it.first << ": " << it.second << endl;return 0;
}

unordered_set

int main()
{unordered_set<int> s = { 1,2,3,4,5,6 };s.erase(2);auto pos = s.find(5);s.erase(pos);for (auto x : s)cout << x << " ";cout << endl;return 0;
}

在这里插入图片描述

2.2.4.2 clear 函数

和其他容器的 clear 函数一样,都是清空容器,而非销毁容器。

2.3 C++ 手册中的 unordered_mapunordered_set

在这里插入图片描述

在这里插入图片描述
我们可以看到,它们的模板参数除了 K、T,还多了 Hash(允许你自定义哈希函数),Pred(允许你自定义比较函数),Alloc(允许你自定义申请资源的方式)。
这里我们来讲讲自定义哈希函数自定义比较函数

2.3.1 自定义哈希函数

其实就是通过仿函数重定义操作,直接看代码吧。

#include <iostream>
#include <unordered_set>
#include <functional>struct Point {int x, y;bool operator==(const Point& other) const {return x == other.x && y == other.y;}
};// 自定义哈希函数
namespace std {template <>struct hash<Point> {size_t operator()(const Point& p) const {// 结合 x 和 y 的哈希值生成唯一的哈希码return hash<int>()(p.x) ^ (hash<int>()(p.y) << 1);}};
}int main() {std::unordered_set<Point> pointSet;pointSet.emplace(Point{ 1, 2 });pointSet.emplace(Point{ 3, 4 });if (pointSet.find(Point{ 1, 2 }) != pointSet.end()) {std::cout << "Point (1, 2) exists in the set." << std::endl;}
}

2.3.2 自定义比较函数

#include <iostream>
#include <unordered_map>
using namespace std;// 用户定义类型
struct User {string name;int age;// 构造函数初始化成员变量User(string _name, int _age) : name(_name), age(_age) {}bool operator==(const User& other) const {return name == other.name && age == other.age;}
};// 散列函数
namespace std {template <>struct hash<User> {size_t operator()(const User& u) const {return hash<string>()(u.name) ^ hash<int>()(u.age);}};
}int main() {unordered_map<User, int, hash<User>> userMap;// 插入一些数据userMap[{User("John", 30)}] = 1;userMap[{User("Jane", 25)}] = 2;// 查找某个用户是否存在auto searchKey = User("John", 30);if (userMap.find(searchKey) != userMap.end()) {cout << "Found John!" << endl;}return 0;
}

相关文章:

  • CQF预备知识:Python相关库 -- NumPy 基础知识 - ndarray 索引
  • vue3组件--无限滚动效果
  • Android7 Input(九)View 建立Input Pipeline
  • 15 dart类(get,set,静态,继承,抽象,接口,混入)
  • Gartner报告解读《Technical Professionals Need to Track 5 ImportantLLM Developments》
  • 论文审稿之我对SCI写作的思考
  • CSS之元素定位
  • 批量获取电商商品数据的解决方案|API接口自动化商品采集|item_get 接口详解
  • 动态规划算法:字符串类问题(2)公共串
  • 【电子通识】FPC连接器组成部分与不良案例术语
  • Day02
  • 嵌入式学习笔记——day25
  • 英伟达破局1000 Token/秒!Llama 4以光速重塑AI推理边界
  • 【深度学习】1. 感知器,MLP, 梯度下降,激活函数,反向传播,链式法则
  • 微信小程序 --三剑客
  • STM32的内部FLASH
  • 「OC」源码学习——KVO底层原理探究
  • 30字速成Docker安装与配置指南
  • urdf文件和DH模型参数是一一对应的吗??
  • PySide6 GUI 学习笔记——常用类及控件使用方法(常用类图标QIcon)
  • 专业网站建设哪家权威/外贸推广
  • 太原网站建设优化/云搜索下载
  • 黑龙江省住房和建设厅网站首页/艾滋病阻断药
  • 台州建设公司网站/怎么在网上做推广
  • 手机壳图案设计网站/百度网址大全网站大全
  • 网站模板制作流程/保定网站推广公司