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

 更新时间:2025年11月11日 11:16:55   作者:MzKyle  
std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unordered_set的使用,感兴趣的可以了解一下

一、概述

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<int> us2 = {1, 2, 3, 4};

    // 3. 范围构造(从vector初始化)
    vector<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会被自动去重
// 遍历元素(顺序不确定)
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() << endl; // 输出:2
cout << "最大容量:" << us.max_size() << endl; // 输出:约1e18(取决于系统)

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

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

方法说明
insert(val)插入元素 val,若已存在则忽略;返回 pair<iterator, 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哈希集合内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • c++ KMP字符串匹配算法

    c++ KMP字符串匹配算法

    大家好,本篇文章主要讲的是c++ KMP字符串匹配算法,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • 通过stringstream实现常用的类型转换实例代码

    通过stringstream实现常用的类型转换实例代码

    在本篇文章里小编给大家分享了关于通过stringstream实现常用的类型转换实例代码内容,需要的朋友们可以参考下。
    2020-04-04
  • C++数据结构分析多态的实现与原理及抽象类

    C++数据结构分析多态的实现与原理及抽象类

    继承就是可以直接使用前辈的属性和方法。自然界如果没有继承,那一切都是处于混沌状态。多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作
    2022-02-02
  • C++使用Muduo库实现英译汉功能

    C++使用Muduo库实现英译汉功能

    Muduo库是一个基于非阻塞IO和事件驱动的C++高并发TCP网络编程库,它是一款基于主从Reactor模型的网络库,本文给大家介绍了C++如何使用Muduo库实现英译汉功能,需要的朋友可以参考下
    2025-05-05
  • C++中priority_queue模拟实现的代码示例

    C++中priority_queue模拟实现的代码示例

    在c++语言中数据结构中的堆结构可以通过STL库中的priority_queue 优先队列来实现,这样做极大地简化了我们的工作量,这篇文章主要给大家介绍了关于C++中priority_queue模拟实现的相关资料,需要的朋友可以参考下
    2021-08-08
  • C++ 学习之旅二 说一说C++头文件

    C++ 学习之旅二 说一说C++头文件

    作为一个二手的.net程序员,你看到了C++头文件一定就犯迷糊了,这到底是个啥玩意。再我纠结了24个小时, google20次,度娘10下,看过10来骗文章以后,我可能稍微开窍了。我对C++头文件总结,与.net比较如下
    2012-11-11
  • C++中的6种构造函数举例详解

    C++中的6种构造函数举例详解

    这篇文章主要介绍了C++中的6种构造函数的相关资料,C++中构造函数用于类对象初始化,类型包括默认构造函数、参数化构造函数、拷贝构造函数等,默认构造函数通常不需要参数,编译器会自动生成,除非存在其他构造函数,需要的朋友可以参考下
    2024-10-10
  • 构造函数不能声明为虚函数的原因及分析

    构造函数不能声明为虚函数的原因及分析

    构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它但析构却不一定,我们往往通过基类的指针来销毁对象
    2013-10-10
  • C++中std::variant的使用详解和实战代码示例

    C++中std::variant的使用详解和实战代码示例

    本文主要介绍了C++中std::variant的使用详解和实战代码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-05-05
  • C语言简易实现扫雷小游戏

    C语言简易实现扫雷小游戏

    这篇文章主要为大家详细介绍了C语言简易实现扫雷小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10

最新评论