STL源码剖析:hashtable

hashtable(散列表)数据结构,在插入、删除、搜寻操作上具有常数时间表现,而且这种表现是以统计为基础的,不需要依赖输入元素的随机性。

使用hash function,可能会使得不同的元素被映射到相同的位置(也就是相同的索引),这就是碰撞问题,通常有以下的解决方法:线性探测、二次探测、开链等做法。

线性探测

负载系数,元素个数除以表格大小,负载系数永远在0-1之间,除非采用开链策略。
当hash function 计算出某个位置的元素,而该位置上的空间不再有用,最简单的办法就是循序往下一一寻找,知道有可用空间。
至于删除,必须使用惰性删除,也就是只标记删除极好,实际删除操作待表格重新整理(rehashing)时再进行,这是因为hash table 中的每一个元素不仅仅在表述它自己,也关系到其他元素的排列。
线性探测方法不怎么好,有primary clustering 问题

二次探测

用于解决primary clustering 问题,如果hash function 计算出的位置已经被使用,那就依序尝试H+i^2(i=1…i)找出空闲的位置。
如果假设表格大小为质数,而且永远保持负载系数在0.5以下(也就是说超过0.5就重新配置并重新整理表格),那么就可以确定每插入一个新元素所需要的探测次数不多于2.
至于编程的复杂度可以使用一些技巧
在这里插入图片描述
二次探测可以消除primary clustering 问题,却可能造成secondary clustering :两个元素经过hash function计算出来的位置相同,则插入位置所探测的位置也相同,形成某种浪费,消除次集团的方法可以有复式散列。

开链

这种做法是在每一个表格元素中维护一个list,hash function 为我们分配某一个list,然后我们在该list执行元素的插入、寻找、删除操作。使用开链手法,表格的负载系数将大于1.
hash 表格的元素称为bucket,bucket聚合体采用vector 完成,以便有动态扩充能力
在这里插入图片描述
开链法并不要求表格大小为质数,但是SGI STL仍然以质数来设计表格大小,并且将28个质数(逐渐呈现2倍的关系)计算,以备随时访问,同时提供一个函数用于查询28个质数中,最接近某数并大于某数的质数。
在这里插入图片描述
在这里插入图片描述
(先不敲代码了,上面合起来是完整的函数)首先判断当前的大小是不是大于bucket.size(),如果是,要找出下一个质数 n,并创建大小为n的新bucket.
使用while(first)是因为一个bucket是有一条链表的 所以重建时要把一个bucket的链表复制过去。
在看的过程中,一度好奇为什么要first->next=tmp[new_bucket],后来自己手动画了一下,理解如下:把当前first节点移到新的bucket中,为了在链表中不丢失first的下一个节点,要先保存 first->next ,于是可以处理当前节点first, first->next=tmp[new_bucket]是指 旧的bucket当前first节点 的下一个节点是 tmp[new_bucket]中的首节点 ,tmp[new_bucket]=first 把新容器中的首节点变更为 旧容器的first节点,最后将保存的原先的first->next赋值给first,再接着循环。最后交换bucket,示意图如下
在这里插入图片描述
在这里插入图片描述
上面这段代码的意思是,首先找到要插入的元素的对应bucket位置,在该bucket中会有一条链表,在该链中使用循环寻找是否有相同的元素,如果有则不执行插入。如果没有则将新插入的元素插入bucket所指的链表头部,并维护节点的个数
在这里插入图片描述
在这里插入图片描述
在不需要重建表格时,该代码的意思是,首先找到对应的bucket,在该bucket的链中使用循环寻找是否有相同的键值,如果有,则马上插入,插入的位置是找到相同键值时的位置。维护节点数目。
如果没有相同元素,则插入到该bucket对应的链表首部。维护节点数目。
在源码中,多次出现的bkt_num是用于判断元素的落脚处。SGI hashtable 无法处理除了char* char 、 const char*、unsigned char、 signed char、 short unsigned short 、int、 unsigned int、 long、 unsigned long ,如果要使用其他类型就要用户自定义hash function,否则会出错。

hash_set

hash_set以hashtable为底层机制,不具有自动排序功能,而set具备。在使用方式上,hash_set和set完全相同。
hashtable无法处理的类型,hash_set也无法处理,在使用上,有的需要提供equal_to的参数,例如

struct eqstr
{
	bool operator()(const char*s1,const char*s2) const
	{
	return strcmp(s1,s2)==0;
	}
};

再把eqstr填入对应的位置。

hash_set本身是不具备排序的功能,但是当bucket数量足够大,有可能会造成排序假象

hash_map

以hashtable为底层机制,不具备自动排序功能,hashtable无法处理的类型,hash_map也无法处理。使用上和map一样

hash_multiset

hash_set和hash_multiset实现上的唯一差别是,前者的元素插入操作采用insert_unique(),后者采用insert_equal().都不能自动排序。

hash_multimap

hash_map和hash_multimap实现上的唯一差别是,前者的元素插入操作采用insert_unique(),后者采用insert_equal().都不能自动排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值