C++:无序关联容器

news2025/3/31 3:47:06

遇到的问题,都有解决方案,希望我的博客能为您提供一点帮助。

一、无序关联容器概述

无序关联容器(如 unordered_setunordered_mapunordered_multisetunordered_multimap)基于 ​哈希表(Hash Table)​ 实现,与有序关联容器(如 setmap)的核心区别在于:

  • 无序性:元素不按特定顺序存储,而是通过哈希函数快速定位。
  • 时间复杂度:平均时间复杂度为 ​O(1)​(理想情况下),最坏情况为 ​O(n)(哈希冲突严重时)。

1. 哈希表结构

无序关联容器的底层是一个 ​动态数组(桶数组)​,每个桶(Bucket)存储一个链表或另一哈希结构(如红黑树)来处理冲突。

  • 桶数组:初始大小为默认值(如 8),随元素插入动态扩容。
  • 哈希函数:将键(Key)映射为桶索引:
    index = hash(key) % bucket_count();

结构示例: 

Bucket 0: Key1 → Key2 → nullptr
Bucket 1: Key3 → nullptr
Bucket 2: nullptr
...
Bucket N: Key4 → Key5 → nullptr

2. 冲突解决策略

C++标准库采用 ​链地址法(Separate Chaining)​

  • 每个桶维护一个链表(单链表),哈希冲突时,元素追加到链表尾部。
  • 示例:插入键 "apple" 和 "apply"(假设哈希值相同):
    桶数组索引:3  
    → 链表节点1: "apple" → 链表节点2: "apply"

3. 负载因子(Load Factor)与扩容

  • 负载因子load_factor = size() / bucket_count()
  • 定义负载因子 = 元素数量 / 桶数量
  • 默认阈值:1.0(超过时触发扩容)。
  • 扩容触发条件:当 load_factor > max_load_factor(默认 1.0)时,触发 rehash
  • 扩容机制
    1. 创建新的桶数组(大小通常为质数,约为原大小的两倍)。
    2. 对所有元素重新计算哈希值,并插入新桶。
    3. 旧桶链表节点被逐个移动到新桶(无需重建元素对象)。

4、无序容器的优势与使用场景

4.​1. 优势
  • 性能优势:哈希表在理想情况下(低冲突)提供常数时间操作,适合高频操作。
  • 简化代码:无需定义关键字的比较运算符(仅需哈希函数和 ==)。
​4.2. 何时选择无序容器?
  • 关键字类型无自然顺序(如 UUID、随机生成的数据)。
  • 性能测试表明哈希技术能显著提升效率。
  • 无需维护元素顺序,且希望减少插入/查找时间。

二、无序容器的操作与示例

1、支持的操作

无序容器提供与有序容器相同的接口,包括:

  • 插入insertemplace
  • 查找findcount
  • 删除erase
  • 遍历:迭代器(但顺序不确定)

2、STL中的无序容器接口

2.1. 容器定义

#include <unordered_set>
#include <unordered_map>

// 定义示例
std::unordered_set<int> uset;                  // 唯一键集合
std::unordered_map<std::string, int> umap;      // 键值对集合
std::unordered_multiset<int> umset;            // 允许重复键的集合
std::unordered_multimap<std::string, int> ummap; // 允许重复键的键值对集合

2.2. 插入元素

  • insert:插入键或键值对,返回是否成功(对unordered_set/map)。

  • emplace:直接构造元素,避免拷贝。

  • operator[](仅unordered_map):若键不存在,插入默认值;存在则返回引用。

示例

umap.insert({"apple", 5});          // 插入键值对
umap.emplace("banana", 3);         // 直接构造元素
umap["orange"] = 8;                // 使用operator[]插入或修改

2.3. 查找元素

  • find:返回指向元素的迭代器,未找到返回end()

  • count:返回键的出现次数(对multi容器有效)。

  • equal_range:返回匹配键的范围迭代器对。

示例

auto it = umap.find("apple");
if (it != umap.end()) {
    std::cout << "Found: " << it->second << std::endl;
}

size_t cnt = ummap.count("apple"); // 统计键出现的次数

2.4. 删除元素

  • erase:通过迭代器、键或范围删除元素。

  • clear:清空所有元素。

示例

umap.erase("apple");               // 删除键为"apple"的元素
auto it = umap.find("banana");
if (it != umap.end()) {
    umap.erase(it);                // 通过迭代器删除
}
umap.clear();                      // 清空容器

2.5. 桶管理接口

  • bucket_count():返回当前桶的数量。

  • bucket_size(n):返回第n个桶中的元素数量。

  • bucket(key):返回键所在的桶索引。

2.5.1. 桶接口
函数作用
c.bucket_count()返回当前使用的桶数量(非最大值)。
c.max_bucket_count()返回容器支持的最大桶数量(受实现或内存限制)。
c.bucket_size(n)返回第 n 个桶中的元素数量(用于检查桶负载情况)。
c.bucket(k)返回键 k 所在的桶索引(用于定位元素分布)。

2.5.2. 桶迭代
类型/函数作用
local_iterator遍历单个桶元素的迭代器(非 const 版本)。
const_local_iterator遍历单个桶元素的常量迭代器(不可修改元素)。
c.begin(n), c.end(n)返回第 n 个桶的起始和结束迭代器(用于遍历桶内元素)。
c.cbegin(n), c.cend(n)返回第 n 个桶的常量起始和结束迭代器。
size_t buckets = umap.bucket_count();
size_t bucket_idx = umap.bucket("apple");
size_t elements_in_bucket = umap.bucket_size(bucket_idx);
2.5.3. 哈希策略
函数/操作作用
c.load_factor()返回当前平均每个桶的元素数量(size() / bucket_count())。
c.max_load_factor()返回容器试图维持的最大负载因子(默认通常为 1.0)。
c.rehash(n)重组哈希表,使桶数量至少为 n,并满足 bucket_count > size() / max_load_factor
c.reserve(n)预留空间,使容器可保存 n 个元素而无需 rehash(自动调整桶数量)。

关键逻辑

  • 当 load_factor() > max_load_factor() 时,容器自动增加桶数量(触发 rehash)。

  • rehash(n) 强制重组哈希表,适用于预知元素数量增长的场景。

  • reserve(n) 等价于 rehash(ceil(n / max_load_factor())),避免频繁重组。


三、自定义哈希与比较函数

1. 自定义哈希函数

为自定义类型作为键时,需提供哈希函数。哈希函数需满足:

  • 相同输入产生相同哈希值。

  • 不同输入尽可能产生不同哈希值(减少冲突)。

示例

struct Person {
    std::string name;
    int age;
};

// 自定义哈希函数
struct PersonHash {
    size_t operator()(const Person& p) const {
        return std::hash<std::string>()(p.name) ^ std::hash<int>()(p.age);
    }
};

std::unordered_set<Person, PersonHash> person_set;
2. 自定义相等比较

默认使用operator==,可自定义相等谓词。

struct PersonEqual {
    bool operator()(const Person& a, const Person& b) const {
        return a.name == b.name && a.age == b.age;
    }
};

std::unordered_set<Person, PersonHash, PersonEqual> person_set;
3. 使用std::hash组合(C++17)

通过组合多个哈希值,减少冲突概率。

template <typename T>
void hash_combine(size_t& seed, const T& val) {
    seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

struct PersonHash {
    size_t operator()(const Person& p) const {
        size_t seed = 0;
        hash_combine(seed, p.name);
        hash_combine(seed, p.age);
        return seed;
    }
};

四、性能优化

1. 优化策略
  • 预分配桶数量:减少重哈希次数。

    std::unordered_map<std::string, int> umap(1000); // 初始1000个桶
  • 调整最大负载因子:平衡内存与性能。

    umap.max_load_factor(0.75); // 负载因子超过0.75时触发重哈希
  • 预留空间:提前分配足够桶。

    umap.reserve(5000); // 预留至少5000个元素的空间
2. 注意事项
  • 哈希函数质量:差的哈希函数导致频繁冲突,性能下降。

  • 键的不可变性:插入后修改键可能导致容器状态不一致。

  • 迭代器失效

    • 插入可能触发重哈希,使所有迭代器失效。

    • 删除仅使被删元素的迭代器失效。


五、对比有序关联容器

特性无序关联容器有序关联容器
底层实现哈希表红黑树
时间复杂度平均O(1),最坏O(n)稳定O(log n)
元素顺序无序按键升序排列
内存占用较高(桶数组+链表)较低(树节点)
自定义排序仅哈希函数和相等比较支持自定义比较函数
适用场景快速查找,不关心顺序需要有序遍历或范围查询

六、实际应用示例

1. 统计单词频率

std::unordered_map<std::string, int> word_count;
std::string word;
while (std::cin >> word) {
    ++word_count[word];
}

// 输出结果(无序)
for (const auto& [word, count] : word_count) {
    std::cout << word << ": " << count << std::endl;
}

2. 实现LRU缓存

template <typename Key, typename Value>
class LRUCache {
private:
    using List = std::list<std::pair<Key, Value>>;
    using Map = std::unordered_map<Key, typename List::iterator>;

    List lru_list;
    Map cache_map;
    size_t capacity;

public:
    LRUCache(size_t cap) : capacity(cap) {}

    Value* get(const Key& key) {
        auto it = cache_map.find(key);
        if (it == cache_map.end()) return nullptr;

        // 移动访问项到链表头部
        lru_list.splice(lru_list.begin(), lru_list, it->second);
        return &(it->second->second);
    }

    void put(const Key& key, const Value& value) {
        auto it = cache_map.find(key);
        if (it != cache_map.end()) {
            // 更新现有项并移至头部
            lru_list.erase(it->second);
        }

        // 插入新项到头部
        lru_list.emplace_front(key, value);
        cache_map[key] = lru_list.begin();

        // 超出容量则移除尾部
        if (cache_map.size() > capacity) {
            cache_map.erase(lru_list.back().first);
            lru_list.pop_back();
        }
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2323228.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3.27学习总结 算法题

自己用c语言做的&#xff0c;不尽如意 后面看了题解&#xff0c;用的是c&#xff0c;其中string 变量和字符串拼接感觉比c方便好多&#xff0c;可以用更少的代码实现更好的效果&#xff0c;打算之后去学习c&#xff0c;用c写算法。 递归&#xff0c;不断输入字符&#xff0c;…

案例分享|树莓派媒体播放器,重构商场广告的“黄金三秒”

研究显示&#xff0c;与传统户外广告相比&#xff0c;数字户外广告在消费者心中的记忆率提高了17%&#xff0c;而动态户外广告更是能提升16%的销售业绩&#xff0c;整体广告效率提升了17%。这一显著优势&#xff0c;使得越来越多资源和技术流入数字广告行业。 户外裸眼3D广告 无…

Redisson - 分布式锁和同步器

文章目录 锁&#xff08;Lock&#xff09;公平锁&#xff08;Fair Lock&#xff09;联锁&#xff08;MultiLock&#xff09;红锁&#xff08;RedLock&#xff09; 【已废弃】读写锁&#xff08;ReadWriteLock&#xff09;信号量&#xff08;Semaphore&#xff09;可过期许可信号…

Zustand 状态管理:从入门到实践

Zustand 状态管理&#xff1a;从入门到实践 Zustand 是一个轻量、快速且灵活的 React 状态管理库。它基于 Hooks API&#xff0c;提供了简洁的接口来创建和使用状态&#xff0c;同时易于扩展和优化。本文将通过一个 TODO 应用实例带你快速入门 Zustand&#xff0c;并探讨其核心…

PGP实现简单加密教程

模拟情景&#xff1a; 假设001和002两位同学的电脑上都安装了PGP&#xff0c;现在两人需要进行加密通讯。 一、创建密钥 1.新建密钥&#xff0c;输入名称和邮箱&#xff0c;输入8位口令&#xff0c;根据指示完成。 2.将其添加到主密钥&#xff0c;鼠标右击出现选项。 这里出…

7.8 窗体间传递数据

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的 当项目中有多个窗体时&#xff08;在本节中为两个窗体&#xff1a;Form1和Form2&#xff09;&#xff0c;窗体间传递数据有以下几种方…

【redis】集群 数据分片算法:哈希求余、一致性哈希、哈希槽分区算法

文章目录 什么是集群数据分片算法哈希求余分片搬运 一致性哈希扩容 哈希槽分区算法扩容相关问题 什么是集群 广义的集群&#xff0c;只要你是多个机器&#xff0c;构成了分布式系统&#xff0c;都可以称为是一个“集群” 前面的“主从结构”和“哨兵模式”可以称为是“广义的…

基于Springboot的网上订餐系统 【源码】+【PPT】+【开题报告】+【论文】

网上订餐系统是一个基于Java语言和Spring Boot框架开发的Web应用&#xff0c;旨在为用户和管理员提供一个便捷的订餐平台。该系统通过简化餐饮订购和管理流程&#xff0c;为用户提供快速、高效的在线订餐体验&#xff0c;同时也为管理员提供完善的后台管理功能&#xff0c;帮助…

【redis】集群 如何搭建集群详解

文章目录 集群搭建1. 创建目录和配置2. 编写 docker-compose.yml完整配置文件 3. 启动容器4. 构建集群超时 集群搭建 基于 docker 在我们云服务器上搭建出一个 redis 集群出来 当前节点&#xff0c;主要是因为我们只有一个云服务器&#xff0c;搞分布式系统&#xff0c;就比较…

飞牛NAS本地部署小雅Alist结合内网穿透实现跨地域远程在线访问观影

文章目录 前言1. VMware安装飞牛云&#xff08;fnOS&#xff09;1.1 打开VMware创建虚拟机1.3 初始化系统 2. 飞牛云搭建小雅Alist3. 公网远程访问小雅Alist3.1 安装Cpolar内网穿透3.2 创建远程连接公网地址 4. 固定Alist小雅公网地址 前言 嘿&#xff0c;小伙伴们&#xff0c…

Linux版本控制器Git【Ubuntu系统】

文章目录 **前言**一、版本控制器二、Git 简史三、安装 Git四、 在 Gitee/Github 创建项目五、三板斧1、git add 命令2、git commit 命令3、git push 命令 六、其他1、git pull 命令2、git log 命令3、git reflog 命令4、git stash 命令 七、.ignore 文件1、为什么使用 .gitign…

browser-use 库网页元素点击测试工具

目录 代码代码解释输出结果 代码 import asyncio import jsonfrom browser_use.browser.browser import Browser, BrowserConfig from browser_use.dom.views import DOMBaseNode, DOMElementNode, DOMTextNode from browser_use.utils import time_execution_syncclass Eleme…

解决GitLab无法拉取项目

1、验证 SSH 密钥是否已生成 ls ~/.ssh/ 如果看到类似 id_rsa 和 id_rsa.pub 的文件&#xff0c;则说明已存在 SSH 密钥。 避免麻烦&#xff0c;铲掉重来最方便。 如果没有&#xff0c;请生成新的 SSH 密钥&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexam…

FPGA学习篇——Verilog学习之寄存器的实现

1 寄存器理论 这里在常见的寄存器种加了一个复位信号sys_rst_n。&#xff08;_n后缀表示复位信号低电平有效&#xff0c;无这个后缀的则表示高电平有效&#xff09; 这里规定在时钟的上升沿有效&#xff0c;只有当时钟的上升沿来临时&#xff0c;输出out 才会改变&#xff0c;…

【VUE】ant design vue实现表格table上下拖拽排序

适合版本&#xff1a;ant design vue 1.7.8 实现效果&#xff1a; 代码&#xff1a; <template><div class"table-container"><a-table:columns"columns":dataSource"tableData":rowKey"record > record.id":row…

Vue实现动态数据透视表(交叉表)

需求:需要根据前端选择的横维度、竖维度、值去生成一个动态的表格&#xff0c;然后把交叉的值放入到对应的横维度和竖维度之下&#xff0c;其实就是excel里面的数据透视表功能&#xff0c;查询交叉语句为sql语句。 实现页面&#xff1a; 选择一下横维度、竖维度、值之后点击查…

推荐《人工智能算法》卷1、卷2和卷3 合集3本书(附pdf电子书下载)

今天&#xff0c;咱们就一同深入探讨人工智能算法的卷1、卷2和卷3&#xff0c;看看它们各自蕴含着怎样的奥秘&#xff0c;并且附上各自的pdf电子版免费下载地址。 《人工智能算法&#xff08;卷1&#xff09;&#xff1a;基础算法》 下载地址&#xff1a;https://www.panziye…

元宇宙浪潮下,数字孪生如何“乘风破浪”?

在当今科技飞速发展的时代&#xff0c;元宇宙的概念如同一颗璀璨的新星&#xff0c;吸引了全球的目光。元宇宙被描绘为一个平行于现实世界、又与现实世界相互影响且始终在线的虚拟空间&#xff0c;它整合了多种前沿技术&#xff0c;为人们带来沉浸式的交互体验。而数字孪生&…

数据分析 之 怎么看懂图 一

韦恩图怎么看 ①颜色:不同颜色代表不同的集合 ②)颜色重叠部分:表示相交集合共有的元素 ③颜色不重叠的部分:表示改集合独有的元素 ④数字:表示集合独有或共有的元素数量 ⑤百分比:表示该区域元素数占整体的比例 PCA图怎么看 ① 第一主成分坐标轴及主成分贡献率主成分贡献…

手写数据库MYDB(一):项目启动效果展示和环境配置问题说明

1.项目概况 这个项目实际上就是一个轮子项目&#xff0c;现在我看到的这个市面上面比较火的就是这个首先RPC&#xff0c;好多的机构都在搞这个&#xff0c;还有这个消息队列之类的&#xff0c;但是这个是基于MYSQL的&#xff0c;我们知道这个MYSQL在八股盛宴里面是重点考察对象…