C++中unordered_set哈希集合的实现

2025-11-11 22:50

本文主要是介绍C++中unordered_set哈希集合的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder...

一、概述

std::unordered_set 是 C++ 标准库(C++11 引入)中的无序容器,用于存储唯一的元素(无重复值),底层基于哈希表(哈希桶) 实现。与 std::set(基于红黑树,元素有序)相比,它的核心特点是:

  • 无序性:元素存储顺序与插入顺序无关,不支持按Key排序访问;
  • 高效性:平均情况下,插入、删除、查找操作的时间复杂度为 O(1)(最坏情况为 O(n),取决于哈希函数质量);
  • 唯一性:容器中不会存储重复元素,插入重复值会被忽略。

二、头文件与命名空间

使用 std::unordered_set 需包含头文件 <unordered_set>,并位于 std 命名空间中:

#include <unordered_set>
using namespace std; // 或显式使用 std::unordered_set

三、常用方法与示例

1. 构造与析构

std::unordered_set 提供多种构造方式,满足不同初始化需求:

方法说明
unordered_set()默认构造:创建空容器
unordered_set(initializer_list<T> init)初始化列表构造:用 {a, b, c} 初始化
unordered_set(InputIt first, InputIt last)范围构造:用迭代器范围 [first, last) 初始化
unordered_set(const unordered_set& other)拷贝构造

示例

#include <IOStream>
#include <unordered_set>
#include <vector>

int main() {
    // 1. 默认构造
    unordered_set<int> us1;

    // 2. 初始化列表构造
    unordered_set<NvPTxUS;int> us2 = {1, 2, 3, 4};

    // 3. 范围构造(从vector初始化)
    vector<China编程;int> vec = {5, 6, 7};
    unordered_set<int> us3(vec.begin(), vec.end());

    // 4. 拷贝构造
    unordered_set<int> us4(us2);

    return 0;
}

2. 迭代器与遍历

std::unordered_set 提供迭代器用于遍历元素,由于无序性,遍历顺序与插入顺序无关。

方法说明
begin() / cbegin()返回指向首元素的迭代器(cbegin() 为 const 版本)
end() / cend()返回指向尾后位置的迭代器

示例

unordered_set<int> us = {3, 1, 4, 1, 5}; // 重复的1会被自动js去重
// 遍历元素(顺序不确定)
for (auto it = us.begin(); it != us.end(); ++it) {
    cout << *it << " "; // 可能输出:3 1 4 5 
}
cout << endl;

// C++11 范围for循环(更简洁)
for (int val : us) {
    cout << val << " "; // 输出顺序与上相同(同一次运行中)
}

3. 容量相关

用于判断容器状态或获取元素数量:

方法说明
empty()判断容器是否为空(空返回 true)
size()返回当前元素个数
max_size()返回容器理论上可存储的最大元素个数(受系统限制)

示例

unordered_set<string> us = {"apple", "banana"};
cout << "是否为空:" << (us.empty() ? "是" : "否") << endl; // 输出:否
cout << "元素个数:" << us.size() << enChina编程dl; // 输出:2
cout << "最大容量:" << us.max_size() << endl; // 输出:约1e18(取决于系统)

4. 元素修改(插入、删除、清空)

这是 std::unordered_set 最核心的操作,用于维护容器中的元素。

方法说明
insert(val)插入元素 val,若已存在则忽略;返回 pair<iteNvPTxUSrator, bool>(迭代器指向元素,bool 表示是否插入成功)
insert(first, last)插入迭代器范围 [first, last) 中的元素
erase(val)删除值为 val 的元素,返回删除的个数(0 或 1)
erase(it)删除迭代器 it 指向的元素,返回下一个元素的迭代器
clear()清空所有元素
swap(other)交换当前容器与 other 的内容

示例

unordered_set<int> us;

// 插入元素
auto res1 = us.insert(10); 
cout << "插入10:" << (res1.second ? "成功" : "失败") << endl; // 成功

auto res2 = us.insert(10); // 插入重复值
cout << "再次插入10:" << (res2.second ? "成功" : "失败") << endl; // 失败

// 插入多个元素
us.insert({20, 30, 40});

// 删除元素(按值)
int del_count = us.erase(20);
cout << "删除20的个数:" << del_count << endl; // 1

// 删除元素(按迭代器)
auto it = us.find(30); // 先查找元素
if (it != us.end()) {
    us.erase(it); // 删除30
}

// 清空容器
us.clear();
cout << "清空后大小:" << us.size() << endl; // 0

5. 元素查找

用于判断元素是否存在或获取元素位置:

方法说明
find(val)查找值为 val 的元素,返回指向该元素的迭代器;若不存在,返回 end()
count(val)返回值为 val 的元素个数(0 或 1,因元素唯一)
contains(val)C++20 新增,判断元素 val 是否存在(返回 bool),比 count() 更直观

示例

unordered_set<string> fruits = {"apple", "banana", "cherry"};

// 查找元素
auto it = fruits.find("banana");
if (it != fruits.end()) {
    cout << "找到:" << *it << endl; // 找到:banana
} else {
    cout << "未找到" << endl;
}

// 计数(判断存在性)
if (fruits.count("orange") == 1) {
    cout << "存在orange" << endl;
} else {
    cout << "不存在orange" << endl; // 输出此句
}

// C++20 contains
if (fruits.contains("apple")) {
    cout << "存在apple" << endl; // 输出此句
}

6. 哈希策略相关

std::unordered_set 底层依赖哈希表,以下方法用于控制哈希表的性能:

方法说明
load_factor()返回当前负载因子(元素数 / 桶数),反映哈希表的拥挤程度
max_load_factor()返回或设置最大负载因子(默认值通常为 1.0)。当实际负载因子超过此值时,哈希表会自动扩容(重哈希)
rehash(n)强制将桶数设置为至少 n,可能触发重哈希
reserve(n)预分配空间,确保容器可容纳 n 个元素而无需重哈希(比 rehash() 更常用)

示例

unordered_set<int> us;

// 预分配空间(避免频繁重哈希)
us.reserve(1000); // 确保可容纳1000个元素

// 插入元素
for (int i = 0; i < 500; ++i) {
    us.insert(i);
}

cout << "当前负载因子:" << us.load_factor() << endl; // ~0.5(500/1000)
cout << "最大负载因子:" << us.max_load_factor() << endl; // 1.0

// 修改最大负载因子
us.max_load_factor(0.8);
cout << "修改后最大负载因子:" << us.max_load_factor() << endl; // 0.8

四、自定义类型的使用

std::unordered_set 存储自定义类型(如结构体)时,需满足两个条件:

  1. 提供哈希函数:告诉容器如何计算元素的哈希值;
  2. 提供相等比较函数:用于解决哈希碰撞(不同元素可能有相同哈希值)。

示例:存储自定义 Person 结构体

#include <string>

struct Person {
    string name;
    int age;

    // 定义相等比较(用于解决哈希碰撞)
    bool operator==(const Person& other) const {
        return name == other.name && age == other.age;
    }
};

// 特化 std::hash 用于 Person(提供哈希函数)
namespace std {
    template<> struct hash<Person> {
        size_t operator()(const Person& p) const {
            // 组合 name 和 age 的哈希值(简单实现)
            size_t h1 = hash<string>()(p.name);
            size_t h2 = hash<int>()(p.age);
            return h1 ^ (h2 << 1); // 哈希组合
        }
    };
}

int main() {
    unordered_set<Person> people;
    people.insert({"Alice", 25});
    people.insert({"Bob", 30});

    // 查找
    Person target = {"Alice", 25};
    if (people.contains(target)) {
        cout << "找到 Alice" << endl;
    }
    return 0;
}

五、与 std::set 的对比

特性std::unordered_setstd::set
底层实现哈希表红黑树(平衡二叉树)
元素顺序无序有序(默认升序)
插入/删除/查找复杂度平均 O(1),最坏 O(n)O(log n)
内存占用较高(哈希表需额外空间)较低
适用场景频繁查找、不关心顺序需要有序遍历、范围查询(如 lower_bound)

六、注意事项

  1. 哈希函数质量:差的哈希函数会导致大量碰撞,使性能退化到 O(n),需确保哈希值分布均匀;
  2. 元素不可修改std::unordered_set 的元素是 const 类型(修改会破坏哈希表结构),若需修改,需先删除再插入;
  3. 重哈希开销:当负载因子超过阈值时,容器会自动重哈希(重建哈希表),可能导致性能波动,可通过 reserve() 提前分配空间避免;
  4. 自定义类型要求:必须提供哈希函数和相等比较函数,否则编译报错。

到此这篇关于C++中unordered_set哈希集合的实现的文章就介绍到这了,更多相关C++ unordered_set哈希集合内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于C++中unordered_set哈希集合的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156136

相关文章

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

通过React实现页面的无限滚动效果

《通过React实现页面的无限滚动效果》今天我们来聊聊无限滚动这个现代Web开发中不可或缺的技术,无论你是刷微博、逛知乎还是看脚本,无限滚动都已经渗透到我们日常的浏览体验中,那么,如何优雅地实现它呢?... 目录1. 早期的解决方案2. 交叉观察者:IntersectionObserver2.1 Inter