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哈希集合内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 关于VS+QT5应用程序换图标的解决方案

    关于VS+QT5应用程序换图标的解决方案

    这篇文章主要介绍了VS+QT5应用程序换图标的处理方案,本文给大家提供了两种解决方案供大家参考,每种方法给大家讲解的都非常详细,需要的朋友可以参考下
    2021-12-12
  • 简单谈谈C语言中的= 和==、!=

    简单谈谈C语言中的= 和==、!=

    这篇文章主要给大家介绍了关于C语言中= 和==、!=的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • C/C++实现枚举网上邻居信息的示例详解

    C/C++实现枚举网上邻居信息的示例详解

    在Windows系统中,通过网络邻居可以方便地查看本地网络中的共享资源和计算机,本文将介绍一个简单的C++程序,使用Windows API枚举网络邻居信息,并获取对端名称、本机名称、主机名称以及主机IP等信息,文中通过代码示例给大家讲解非详细,需要的朋友可以参考下
    2023-12-12
  • 一篇文章带你了解论C语言中算法的重要性

    一篇文章带你了解论C语言中算法的重要性

    最近一直在学数据结构与算法,深深的感受到我们学习语言,永远都只是一项工具,方法才是其中最重要的部分。这篇文章我将会通过几个例子来说明算法,也就是写程序的思路在程序中的重要意义
    2021-08-08
  • c与c++之间的相互调用及函数区别示例详解

    c与c++之间的相互调用及函数区别示例详解

    这篇文章主要为大家介绍了c与c++相互调用的使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Matlab实现数据的动态显示方法

    Matlab实现数据的动态显示方法

    这篇文章主要为大家详细介绍了Matlab使用Plot函数实现数据动态显示方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • C++学习进阶之Makefile基础用法详解

    C++学习进阶之Makefile基础用法详解

    Makefile 通常指的是一个含有一系列命令(directive)的,通过 Make自动化编译工具,帮助 C/C++ 程序实现自动编译目标文件的文件,这篇文章主要给大家介绍了关于C++学习进阶之Makefile基础用法的相关资料,需要的朋友可以参考下
    2021-07-07
  • C++/Php/Python 语言执行shell命令的方法(推荐)

    C++/Php/Python 语言执行shell命令的方法(推荐)

    下面小编就为大家带来一篇C++/Php/Python 语言执行shell命令的方法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • C++详解非类型模板参数Nontype与Template及Parameters的使用

    C++详解非类型模板参数Nontype与Template及Parameters的使用

    除了类型可以作为模板参数,普通值也可以作为模板函数,即非类型模板参数(Nontype Template Parameters)。下面让我们一起了解一下
    2022-06-06
  • opencv实现机器视觉检测和计数的方法

    opencv实现机器视觉检测和计数的方法

    在机器视觉中,有时需要对产品进行检测和计数。其难点无非是对于产品的图像分割。本文就来介绍一下机器视觉检测和计数的实现,感兴趣的可以参考一下
    2021-05-05

最新评论