侯捷 C++ STL标准库和泛型编程 —— 3 容器(关联式容器)

news2024/11/30 2:48:21

3.3 关联式容器

3.3.0 RB-Tree

红黑树(Red-Black Tree)是一种自平衡的二叉搜索树 BST(AVL 是另一种)

rb-tree 提供遍历操作iterators,按中序遍历遍历,便可以得到排序状态

不能用 iterator 去改变元素的 key(其有严谨的排列规则)

rb-tree 提供两种 insertion 操作:insert_unique()insert_equal(),前者表示 key 独一无二,后者表示 key 可重复

GCC2.9下:

image-20230830083207175
template<class Key, // key的类型
		 class Value, // Value里包含key和date
		 class KeyOfValue, // 从Value中取出key的仿函数
		 class Compare, // 比较key大小的仿函数
		 class Alloc = alloc>
class rb_tree
{
protected:
	typedef __rb_tree_node<Value> rb_tree_node;
	...
public:
	typedef rb_tree_node* link_type;
	...
protected:
	size_type node_count; // rb-tree节点数量,大小4
	link_type header; // 头指针,大小4
	Compare Key_compare; // key比大小的仿函数,大小1
    // sizeof: 9 ——> 12(填充到4的倍数)
    ...
};

GCC4.9下:

image-20230830093745761

_M_color 是 “枚举”(Enumeration)

3.3.1 set / multiset
测试
image-20230819161037868
void test_multiset(long& value)
{
    cout << "\ntest_multiset().......... \n";
     
    multiset<string> c;  // 创建一个multiset  	
    char buf[10];		
    clock_t timeStart = clock();  // 记录起始时间							
    for(long i=0; i< value; ++i)  // 添加元素到multiset中
    {
        try {
            snprintf(buf, 10, "%d", rand());  // 将随机数转换为字符串格式
            c.insert(string(buf));  // 将字符串插入multiset中     				
        }
        catch(exception& p) {  // 捕获可能的异常
            cout << "i=" << i << " " << p.what() << endl;  // 输出异常信息
            abort();  // 终止程序
        }
    }
    cout << "毫秒数 : " << (clock()-timeStart) << endl;  // 输出时间差,计算插入时间	
    cout << "multiset.size()= " << c.size() << endl;  // 输出multiset大小	
    cout << "multiset.max_size()= " << c.max_size() << endl;  // 输出multiset的最大容量
    
    string target = get_a_target_string();	
    {
        timeStart = clock();
        auto pItem = find(c.begin(), c.end(), target);  // 在multiset中使用 std::find(...) 查找目标字符串
        cout << "std::find(),毫秒数 : " << (clock()-timeStart) << endl;		
        ...
    }
 	
    {
        timeStart = clock();		
        auto pItem = c.find(target);  // 在multiset中使用 c.find(...) 查找目标字符串
        cout << "c.find(),毫秒数 : " << (clock()-timeStart) << endl;		 
        ...
    }	
	 
    c.clear();  // 清空multiset
}

安插元素是使用 insert(),其位置由红黑树决定

容器自己有 c.find(),其会比全局的 ::find()

运行结果:

image-20230819162112550

随机数据填充容器:6609ms(其在填充的时候就进行排序了);直接搜索 ::find():203ms;c.find():0ms


深度探索

以 rb-tree 为底层结构,因此有——元素自动排序,key 与 value 和一

set / multiset 提供遍历操作iterators,按中序遍历遍历,便可以得到排序状态

禁止用 iterator 去改变元素的值(其有严谨的排列规则)

set的key 独一无二,其 insert() 操作用的 rb-tree 的:insert_unique()

multiset 的 key 可以重复,其 insert() 操作用的 rb-tree 的:insert_equal()

GCC2.9下:

// set
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set
{
public:
	typedef Key key_type;
	typedef Key value_type;
	typedef Compare key_compare;
	typedef Compare value_compare;
private:
	typedef rb_tree<key_type, value_type, identity<value_type>, 
    			    key_compare, Alloc> rep_type;
	rep_type t; // 采用红黑树作为底层机制
public:
	typedef typename rep_type::const_iterator iterator;
	// 注意:这里是const_iterator,所以不能用iterator改元素
    ...
};
3.3.2 map / multimap
测试
image-20230819162351918
void test_multimap(long& value)
{
    ...
    multimap<long, string> c;  // 创建一个multimap,key 为 long 类型,value 为 string 类型  	
    char buf[10];
    clock_t timeStart = clock();  // 记录起始时间							
    for(long i=0; i< value; ++i)  // 添加元素到multimap中
    {
        try {
            snprintf(buf, 10, "%d", rand());  // 将随机数转换为字符串格式并复制到缓冲区
            // multimap 不可使用 [] 做 insertion 
            c.insert(pair<long, string>(i, buf));  // 将元素插入multimap中   						
        }
        catch(exception& p) {  // 捕获可能的异常
            cout << "i=" << i << " " << p.what() << endl;  // 输出异常信息
            abort();  // 终止程序
        }
    }
    cout << "毫秒数 : " << (clock()-timeStart) << endl;  // 输出时间差,计算插入时间	
    cout << "multimap.size()= " << c.size() << endl;  // 输出multimap大小
    cout << "multimap.max_size()= " << c.max_size() << endl;  // 输出multimap的最大容量
    
    long target = get_a_target_long();		
    timeStart = clock();		
    auto pItem = c.find(target);  // 在multimap中查找目标 key								
    cout << "c.find(),毫秒数 : " << (clock()-timeStart) << endl;	 
    
    if (pItem != c.end())
        cout << "找到,value=" << (*pItem).second << endl;  // 如果找到,输出找到的值
    else
        cout << "未找到!" << endl;  // 如果未找到,输出未找到的信息	
    
    c.clear();  // 清空multimap		  					
}

c.insert(pair<long, string>(i, buf));key 是从1~1000000,value 是随机取的,将其组合为 pair 插入

运行结果:

image-20230819163328911

随机数据填充容器:4812ms(其在填充的时候就进行排序了);c.find():0ms


深度探索

以 rb-tree 为底层结构,因此有——元素自动排序

map/ multimap 提供遍历操作iterators,按中序遍历遍历,便可以得到排序状态

不能用 iterator 去改变元素的key(其有严谨的排列规则),但可以用 iterator 去改变元素的 data

因此 map / multimap 将 user 指定的 key_type 设定成 const

map的key 独一无二,其 insert() 操作用的 rb-tree 的:insert_unique()

multimap 的 key 可以重复,其 insert() 操作用的 rb-tree 的:insert_equal()

GCC2.9下:

template <class Key, // key的类型
		 class T, // data的类型
		 class Compare = less<Key>, 
		 class Alloc = alloc>
class map
{
public:
	typedef Key key_type;
	typedef T data_type;
	typedef T mapped_type;
	typedef pair<const Key, T> value_type;
    // 注意:这里是const Key ———— 防止改key
	typedef Compare key_compare;
private:
	typedef rb_tree<key_type, value_type, select1st<value_type>, key_compare, Alloc> rep_type;
	rep_type t; // 采用红黑树作为底层机制
public:
	typedef typename rep_type::iterator iterator;
	...
};

map 的插入元素有特殊写法:c[i] = string(buf),其中 i 就是 key;multimap没有

map 的 [] 功能:

访问元素: 如果指定的键存在于映射中,map[key] 将返回与该键关联的 data;如果键不存在,map[key]自动创建一个新的键值对,key 为指定的 key,data 为默认 data,并返回这个默认 data

3.3.3 HashTable
image-20230830144746686
  • 元素的位置 = key % bucket大小

  • bucket vector 的大小为质数

  • 元素个数大于 bucket 的总数时,bucket vector 扩充并重新打散放在新计算的 bucket 中(rehashing 很花时间)—— bucket 一定比元素多

    在扩充时,按 vector 扩充为2倍大小,但会选择靠进这个数的一个质数做新的大小

GCC2.9下:

template <class Value, // Value里包含key和date
		  class Key, // key的类型
		  class HashFcn, // hash函数
		  class ExtractKey, // 从Value中取出key的方法
		  class EqualKey, // 判断key相等的函数
		  class Alloc>
class hashtable
{
public:
	typedef HashFcn hasher; 
	typedef EqualKey key_equal; // 判断key相等的函数
	typedef size_t size_type;
private:
    // 3个函数对象,大小一共3(应该是0,因为一些因素)
	hasher hash;
	key_equal equals;
	ExtractKey get_key;

	typedef __hashtable_node<Value> node;

	vector<node*, Alloc> buckets; // vector里3个指针,大小12
	size_type num_elements; // 大小4
    // 一共19 ——> 20(调整为4的倍数)
public:
	size_type bucket_count() const { return buckets.size(); }
};

Hash函数:

偏特化写不同类型的 hash 函数,下图都是数值类型,直接返回就可以

image-20230830153207439

下图对 c 风格的字符串做了处理(也可以自己设计),来生成 hash code

image-20230830153109919

注意:老版本STL没有提供现成的 string 类型的 hash 函数

3.3.4 unordered容器
测试
image-20230818103522538
void test_unordered_multiset(long& value)
{
    cout << "\ntest_unordered_multiset().......... \n";
     
    unordered_multiset<string> c;  // 创建一个 unordered_multiset  	
    char buf[10];
    clock_t timeStart = clock();  // 记录起始时间							
    for(long i=0; i< value; ++i)  // 添加元素到 unordered_multiset 中
    {
        try {
            snprintf(buf, 10, "%d", rand());  // 将随机数转换为字符串格式
            c.insert(string(buf));  // 将字符串插入 unordered_multiset 中   			  		
        }
        catch(exception& p) {  // 捕获可能的异常
            cout << "i=" << i << " " << p.what() << endl;  // 输出异常信息
            abort();  // 终止程序
        }
    }
    cout << "毫秒数 : " << (clock()-timeStart) << endl;  // 输出时间差,计算插入时间	
    cout << "unordered_multiset.size()= " << c.size() << endl;  // 输出 unordered_multiset 大小
    cout << "unordered_multiset.max_size()= " << c.max_size() << endl;  // 输出 unordered_multiset 的最大容量
    cout << "unordered_multiset.bucket_count()= " << c.bucket_count() << endl;  // 输出 unordered_multiset 的桶数量
    cout << "unordered_multiset.load_factor()= " << c.load_factor() << endl;  // 输出 unordered_multiset 的负载因子
    cout << "unordered_multiset.max_load_factor()= " << c.max_load_factor() << endl;  // 输出 unordered_multiset 的最大负载因子
    cout << "unordered_multiset.max_bucket_count()= " << c.max_bucket_count() << endl;  // 输出 unordered_multiset 的最大桶数量
    for (unsigned i=0; i< 20; ++i) {
        cout << "bucket #" << i << " has " << c.bucket_size(i) << " elements.\n";  // 输出前20个桶中的元素数量
    }					
				
    string target = get_a_target_string();	
    {
        timeStart = clock();
        auto pItem = find(c.begin(), c.end(), target);  // 在 unordered_multiset 中使用 std::find(...) 查找目标字符串
        cout << "std::find(),毫秒数 : " << (clock()-timeStart) << endl;	
        if (pItem != c.end())
            cout << "found, " << *pItem << endl;  // 如果找到,输出找到的元素
        else
            cout << "not found! " << endl;  // 如果未找到,输出未找到的信息	
    }
 
    {
        timeStart = clock();		
        auto pItem = c.find(target);  // 在 unordered_multiset 中使用 c.find(...) 查找目标字符串
        cout << "c.find(),毫秒数 : " << (clock()-timeStart) << endl;	 
        if (pItem != c.end())
            cout << "found, " << *pItem << endl;  // 如果找到,输出找到的元素
        else
            cout << "not found! " << endl;  // 如果未找到,输出未找到的信息	
    }		
	 
    c.clear();  // 清空unordered_multiset
}					

运行结果:

image-20230819164416021

随机数据填充容器:4406ms;直接搜索 ::find():109ms;c.find():0ms;前二十个 bucket 中只有一个有24个元素

深度探索
image-20230830155954989

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

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

相关文章

CCC标准——PHY

1.介绍 UWB物理层使用基于频带受限脉冲的脉冲无线电信号波形。UWB物理层主要用于测距&#xff0c;但也可以用于数据通信。在CCC标准中&#xff0c;物理层的具体定义依然基于IEEE 802.15.4z标准中的HRP UWB PHY&#xff0c;支持更高的脉冲重复频率。 对于增强测距设备&#xf…

华为云云耀云服务器L实例评测|云耀云服务器L实例搭建个人镜像站

华为云云耀云服务器L实例评测&#xff5c;云耀云服务器L实例搭建个人镜像站 一、云耀云服务器L实例介绍1.1 云耀云服务器L实例简介1.2 云耀云服务器L实例特点 二、Apache介绍2.1 Apache简介2.2 Apache特点 三、本次实践介绍3.1 本次实践简介3.2 本次环境规划 四、远程登录华为云…

Springboot对MVC、tomcat扩展配置

Springboot在web层的开发基本都是采用Springmvc框架技术&#xff0c;但是Springmvc中的某些配置在boot是没有的&#xff0c;我们就应该根据自己的需求进行对mvc扩展配置 Springboot1.x版本如何配置 通过注解Configuration一个类&#xff0c;继承webmvcconfigureradapter&#…

AutoAnimate - 无需任何配置,一行代码自动为元素添加优雅的过渡动画,可以搭配 Vue / React 和 Sevelt 使用

这个动画库只要一行代码就可以自动在我们的组件中添过渡动画&#xff0c;为什么这么省事高效呢&#xff1f; AutoAnimate 是一个无需任何配置&#xff0c;自动为我们开发的 Web 项目添加平滑过渡动画的 JavaScript 工具库。AutoAnimate 和之前推荐的一些 js 动画库相比&#x…

贪婪的互联网电视让用户忍无可忍,广电总局出手了

广电总局要求电视需要在今年底前实现开机就看电视&#xff0c;开机广告、关机广告将不再被允许&#xff0c;这对于饱受互联网电视无孔不入的广告困扰的消费者来说无疑是一大利好&#xff0c;他们早已无法忍受越来越多的广告。 一、贪婪的互联网电视 互联网电视企业曾以羊毛出在…

Java下的序列化和反序列化(写出和读入)

代码如下&#xff1a; public class MyWork {public static void main(String[] args) throws IOException, ClassNotFoundException {//序列化File f new File("testFile/testObject.txt");ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(…

【聊天系统的优化】RPC方式的优化

RPC方式的优化 聊天系统的中RPC的选择Jsonprotobufmsgpack 聊天系统的中RPC的选择 在RPC方式中&#xff0c;常用的三种方式&#xff1a;Json&#xff0c;protobuf&#xff0c;Msgback 设定一个简单的加和服务&#xff0c;客户端发送一个list给服务端&#xff0c;需要将list的…

<Xcode> Xcode IOS无开发者账号打包和分发

关于flutter我们前边聊到的初入门、数据解析、适配、安卓打包、ios端的开发和黑苹果环境部署&#xff0c;但是对于苹果的打包和分发&#xff0c;我只是给大家了一个链接&#xff0c;作为一个顶级好男人&#xff0c;我认为这样是对大家的不负责任&#xff0c;那么这篇就主要是针…

Centos 7分区失败,进入 dracut 页面,恢复操作

1. 问题场景&#xff1a; 分区失败&#xff0c;重启了虚拟机&#xff0c;导致系统进入 dracut 页面。开机显示 直接回车&#xff0c;等待重启失败的页面 自动进入了 dracut 模式(救援)。 2. 临时解决进入系统 查了一下&#xff1a;如果出现 “dracut” 提示、进入 dracut…

【分布式计算】三、虚拟化 Virtualization

1.什么是虚拟化 1.1.非虚拟化 我们首先来认识什么是非虚拟化   1.一台机器、一个操作系统、几个应用程序   2.应用程序可能会相互影响。   3.机器利用率较低&#xff0c;正常情况下低于25%。 关于X86平台&#xff1a; 1.服务器基础设施利用率低&#xff08;10-18%&#…

【网络协议】IP

当连接多个异构的局域网形成强烈需求时&#xff0c;用户不满足于仅在一个局域网内进行通信&#xff0c;他们希望通过更高一层协议最终实现异构网络之间的连接。既然需要通过更高一层的协议将多个局域网进行互联&#xff0c;那么这个协议就必须为不同的局域网环境定义统一的寻址…

《C和指针》读书笔记(第十四章 预处理器)

目录 0 简介1 预定义符号2 #define2.1 宏2.2 #define替换2.3 宏与函数2.4 带副作用的宏参数2.5 命名约定2.6 #undef2.7 命令行定义 3 条件编译3.1 是否被定义3.2 嵌套指令 4 文件包含4.1 函数库文件包含4.2 本地文件包含4.3 嵌套文件包含 5 其他指令6 总结 0 简介 编译一个C程…

Leetcode 662. 二叉树最大宽度

文章目录 题目代码&#xff08;9.30 首刷看解析&#xff09; 题目 Leetcode 662. 二叉树最大宽度 代码&#xff08;9.30 首刷看解析&#xff09; class Solution { public:int widthOfBinaryTree(TreeNode* root) {unsigned long long res 1;using pr pair<TreeNode*, u…

数据集笔记:芝加哥共享单车OD数据

2013年到2022年3月&#xff0c;芝加哥共享单车OD数据 数据地址&#xff1a;Divvy Data (divvybikes.com)

使用python脚本的时间盲注完整步骤

文章目录 一、获取数据库名称长度二、获取数据库名称三、获取表名总长度四、获取表名五、获取指定表列名总长度六、获取指定表列名七、获取指定表指定列的表内数据总长度八、获取指定表指定列的表内数据 一、获取数据库名称长度 测试环境是bwapp靶场 SQL Injection - Blind - …

【考研数学】高等数学第七模块 —— 曲线积分与曲面积分 | 2. 对坐标的曲线积分(第二类积分)

文章目录 一、曲线积分1.2 对坐标的曲线积分&#xff08;第二类曲线积分&#xff09;1.2.1 问题产生 —— 做功问题1.2.2 对坐标的曲线积分的定义&#xff08;了解&#xff09;1.2.3 对坐标的曲线积分的性质1.2.4 二维空间对坐标的曲线积分计算法1. 定积分法2. 二重积分法&…

YOLOv7改进:结合CotNet Transformer结构

1.简介 京东AI研究院提出的一种新的注意力结构。将CoT Block代替了ResNet结构中的3x3卷积&#xff0c;在分类检测分割等任务效果都出类拔萃 论文&#xff1a;Contextual Transformer Networks for Visual Recognition 论文地址&#xff1a;https://arxiv.org/abs/2107.12292 有…

C进阶--数据的存储

⚙ 1. 数据类型介绍 1.1基本内置类型 ⭕ 整形&#xff1a; char(char又叫短整型)unsigned charsigned charshortunsigned short[int]signed short [int]intunsigned intsigned intlongunsigned long [int]signed long [int] ⭕ 浮点数&#xff1a; float&#xff08;单精度浮…

Linux下C语言操作网卡的几个代码实例?特别实用

前面写了一篇关于网络相关的文章&#xff1a;如何获取当前可用网口。 《简简单单教你如何用C语言列举当前所有网口&#xff01;》 那么如何使用C语言直接操作网口&#xff1f; 比如读写IP地址、读写MAC地址等。 一、原理 主要通过系统用socket()、ioctl()、实现 int sock…

基于arduino的土壤湿度检测

1.总体设计框图 本浇花系统总体上分为硬件和软件两大组成部分。硬件部分包括Arduino UNO开发板、温湿度传感器、通信模块、浇水执行系统和液晶显示等。软件部分包括Android客户端。系统结构如图1所示 本浇花系统总体上分为硬件和软件两大组成部分。硬件部分包括Arduino UN…