STL标准库-hash
技术在于交流、沟通,本文为博主原创文章转载请注明出处并保持作品的完整性
hash的结构图如下图
oject通过hashfunc转换成hashcode然后插入到相应篮子中
hash同rb_tree是一种底层实现,在实际应用中很少能直接用到,但是做为容器的底层实现,所以了解一下还是很有必要的
hash的作用机制可以理解成一个vector上面挂着一些单向链表,vecotr中的元素(篮子buckets)指向单向链表.
hash将传进来的object通过hashfunc转换成相应的数值,然后将这些数值放入指定的篮子中,通常的放入篮子的方法取余操作,
如下面片段

上面的图中我们可以看到该结构一共有53个篮子(vector元素个数),每个篮子下面对应着一个单向链表,hashFunc是取余操作,即value/53,余出几就放在第几个篮子中
如53: 53%53 = 0,那么53应该放在第一个篮子中
55: 55%53 = 2,那么55应该放在第三个篮子中
那么我们放55后又放一个2呢?
2%53 = 2,也应该放在第二个篮子中(在hash上叫做碰撞),这个时候就用到了单向链表,将2串接在55后面
为了保证效率,篮子的个数不应该过多.很多实现方式都是当元素个数大于篮子个数时,扩充篮子,并重新排列
G2.9的扩充规则如下
enum { __stl_num_primes =  };
static const unsigned long __stl_prime_list[__stl_num_primes] =
{
  53ul,         97ul,         193ul,       389ul,       769ul,
  1543ul,       3079ul,       6151ul,      12289ul,     24593ul,
  49157ul,      98317ul,      196613ul,    393241ul,    786433ul,
  1572869ul,    3145739ul,    6291469ul,   12582917ul,  25165843ul,
  50331653ul,   100663319ul,  201326611ul, 402653189ul, 805306457ul,
  1610612741ul, 3221225473ul, 4294967291ul
};
默认的hash的篮子个数为53,然后二倍扩充取最近质数为扩充后的篮子数.如53*2 = 106 最近质数为97
扩充的顺序,跟vector相似.先申请内存,然后重新排列
扩充后的hashtable应该是这样

重新排列:这个时候53%97 = 53 所以53应该放在第54个篮子中
55%97 = 55 放在第56个篮子中
2%97 = 2 放在第三个篮子中
如果继续追加元素当元素个数大于篮子个数时在扩充这个时候会扩充到193个篮子,然后再重新排序.....,以上就是hash的概括知识
我们结合一下上面提到的内容一起看看G2.9的源码实现.现在已经是G4.9.但是4.9有些不好理解
1.定义
template <class _Val, class _Key, class _HashFcn,
class _ExtractKey, class _EqualKey, class _Alloc>
class hashtable { //参数1 _Key key值 参数2 _Value value值 参数3 _HashFcn 获取转换之后key值 参数4 ExtractKey key的萃取机 参数5 _Equal 比较key是否相等 参数6 _Allocator 分配器
public:
typedef _Key key_type;
typedef _Val value_type;
typedef _HashFcn hasher;
typedef _EqualKey key_equal; typedef size_t size_type;//元素个数
typedef ptrdiff_t difference_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference; hasher hash_funct() const { return _M_hash; }//取篮子编号函数,即上面的_HashFcn
key_equal key_eq() const { return _M_equals; }//判断key是否相等 private:
typedef _Hashtable_node<_Val> _Node;//节点
...
private:
hasher _M_hash;
key_equal _M_equals;
_ExtractKey _M_get_key;
vector<_Node*,_Alloc> _M_buckets;//用vector维护buckets
size_type _M_num_elements;//hashtable中list节点个数即元素个数
...
}
2.节点
它有一个成员变量_Node,他的实现如下,就是我上文提到的vector上面挂载的list
//hashtable中链表的节点结构
//类似于单链表的节点结构
template <class _Val>
struct _Hashtable_node
{
_Hashtable_node* _M_next;//指向下一节点
_Val _M_val;//节点元素值
};
3.初始化篮子个数_M_initialize_buckets(size_type __n)
//预留空间,并将其初始化为0
void _M_initialize_buckets(size_type __n)
{
//返回大于n的最小素数__n_buckets(就是上面的53 97 193...)
const size_type __n_buckets = _M_next_size(__n);
//这里调用vector的成员函数reserve
//reserve该函数功能是改变可用空间的大小
//Requests that the vector capacity be at least enough to contain __n_buckets elements.
_M_buckets.reserve(__n_buckets);//vector.reserve
//调用vector的插入函数insert
//在原始end后面连续插入__n_buckets个0
_M_buckets.insert(_M_buckets.end(), __n_buckets, (_Node*) );
_M_num_elements = ;
}
4.hashFunc
//获取键值key在篮子的位置
size_type _M_bkt_num_key(const key_type& __key) const
{
return _M_bkt_num_key(__key, _M_buckets.size());
} //获取在篮子的序号,也就是键值
//输入参数是实值value
size_type _M_bkt_num(const value_type& __obj) const
{
return _M_bkt_num_key(_M_get_key(__obj));
} size_type _M_bkt_num_key(const key_type& __key, size_t __n) const
{
return _M_hash(__key) % __n;//最后都是采取取余
}
5.insert_unique冲入不可重复元素
pair<iterator, bool> insert_unique(const value_type& __obj)
{
//判断容量是否够用, 否则就重新配置
resize(_M_num_elements + );
//插入元素,不允许存在重复元素
return insert_unique_noresize(__obj);
}
resize()
//调整hashtable的容量
//新的容量大小为__num_elements_hint
template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
void hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
::resize(size_type __num_elements_hint)
{
//hashtable原始大小
const size_type __old_n = _M_buckets.size();
if (__num_elements_hint > __old_n) {//若新的容量大小比原始的大
//查找不低于__num_elements_hint的最小素数
const size_type __n = _M_next_size(__num_elements_hint);
if (__n > __old_n) {
//创建新的线性表,容量为__n,只是起到中介作用
vector<_Node*, _All> __tmp(__n, (_Node*)(),
_M_buckets.get_allocator());
__STL_TRY {//以下是复制数据
for (size_type __bucket = ; __bucket < __old_n; ++__bucket) {
_Node* __first = _M_buckets[__bucket];
while (__first) {
//获取实值在新篮子的键值位置
size_type __new_bucket = _M_bkt_num(__first->_M_val, __n);
//这个只是为了方便while循环里面__first的迭代
_M_buckets[__bucket] = __first->_M_next;
//将当前节点插入到新的篮子__new_bucket里面,成为list的第一个节点
__first->_M_next = __tmp[__new_bucket];//__first->_M_next指向null指针,因为新篮子是空的
__tmp[__new_bucket] = __first;//新篮子对应键值指向第一个节点
__first = _M_buckets[__bucket];//更新当前指针
}
}
_M_buckets.swap(__tmp);//交换内容
}
# ifdef __STL_USE_EXCEPTIONS
catch(...) {//释放临时hashtable的线性表tmp
for (size_type __bucket = ; __bucket < __tmp.size(); ++__bucket) {
while (__tmp[__bucket]) {
_Node* __next = __tmp[__bucket]->_M_next;
_M_delete_node(__tmp[__bucket]);
__tmp[__bucket] = __next;
}
}
throw;
}
# endif /* __STL_USE_EXCEPTIONS */
}
}
}
insert_unique_noresize
//插入元素,不需要重新调整内存空间,不允许存在重复元素
template <class _Val, class _Key, class _HF, class _Ex, class _Eq, class _All>
pair<typename hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>::iterator, bool>
hashtable<_Val,_Key,_HF,_Ex,_Eq,_All>
::insert_unique_noresize(const value_type& __obj)
{
//获取待插入元素在hashtable中的篮子位置
const size_type __n = _M_bkt_num(__obj);
_Node* __first = _M_buckets[__n]; //判断hashtable中是否存在与之相等的键值元素
//若存在则不插入
//否则插入该元素
for (_Node* __cur = __first; __cur; __cur = __cur->_M_next)
if (_M_equals(_M_get_key(__cur->_M_val), _M_get_key(__obj)))
return pair<iterator, bool>(iterator(__cur, this), false); //把元素插入到第一个节点位置
_Node* __tmp = _M_new_node(__obj);
__tmp->_M_next = __first;
_M_buckets[__n] = __tmp;
++_M_num_elements;
return pair<iterator, bool>(iterator(__tmp, this), true);
}
6.迭代器
//hashtable迭代器定义
//注意:hash table迭代器没有提供后退操作operator--
//也没用提供逆向迭代器reverse iterator
template <class _Val, class _Key, class _HashFcn,
class _ExtractKey, class _EqualKey, class _Alloc>
struct _Hashtable_iterator {
//内嵌类型别名
typedef hashtable<_Val,_Key,_HashFcn,_ExtractKey,_EqualKey,_Alloc>
_Hashtable;
typedef _Hashtable_iterator<_Val, _Key, _HashFcn,
_ExtractKey, _EqualKey, _Alloc>
iterator;
typedef _Hashtable_const_iterator<_Val, _Key, _HashFcn,
_ExtractKey, _EqualKey, _Alloc>
const_iterator;
typedef _Hashtable_node<_Val> _Node; typedef forward_iterator_tag iterator_category;//采用正向迭代器
typedef _Val value_type;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef _Val& reference;
typedef _Val* pointer; _Node* _M_cur;//当前迭代器所指位置
_Hashtable* _M_ht;//hashtable中的位置,控制访问篮子连续性
...
}
operator++满足自动识别到每个list的末端时跳到下一个篮子
//前缀operator++重载,前进一个list节点
template <class _Val, class _Key, class _HF, class _ExK, class _EqK,
class _All>
_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>&
_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>::operator++()
{
const _Node* __old = _M_cur;
_M_cur = _M_cur->_M_next;//若存在,则返回
//若当前节点为空,则需前进到下一个篮子的节点
if (!_M_cur) {
//根据元素值,定位出下一个bucket的位置,其起始位置就是我们的目的地
size_type __bucket = _M_ht->_M_bkt_num(__old->_M_val);
while (!_M_cur && ++__bucket < _M_ht->_M_buckets.size())
_M_cur = _M_ht->_M_buckets[__bucket];
}
return *this;
} //后缀operator++重载 template <class _Val, class _Key, class _HF, class _ExK, class _EqK,
class _All>
inline _Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>
_Hashtable_iterator<_Val,_Key,_HF,_ExK,_EqK,_All>::operator++(int)
{
iterator __tmp = *this;
++*this;//调用operator++
return __tmp;
}
STL标准库-hash的更多相关文章
- STL标准库-算法-常用算法
		
技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 介绍11种STL标准库的算法,从这11种算法中总结一下算法的基本使用 1.accumulate() 累加 2.for_each( ...
 - STL标准库-容器-set与multiset
		
技术在于交流.沟通,转载请注明出处并保持作品的完整性. set与multiset关联容器 结构如下 set是一种关联容器,key即value,value即key.它是自动排序,排序特点依据key se ...
 - STL标准库-容器-deque
		
技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. deque双向开口可进可出的容器 我们知道连续内存的容器不能随意扩充,因为这样容易扩充别人那去 deque却可以,它创造了内存 ...
 - STL标准库-容器-vector
		
技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. 向量容器vector是一个动态数组,内存连续,它是动态分配内存,且每次扩张的原来的二倍. 他的结构如下 一 定义 vector ...
 - STL标准库-容器-set与map
		
STL标准库-容器-set与multiset C++的set https://www.cnblogs.com/LearningTheLoad/p/7456024.html STL标准库-容器-map和 ...
 - C++STL标准库学习笔记(五)set
		
前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标记了出来,这一篇后面主要都是我的记录了,为了防止大片蓝色字体出现,后面就不改蓝色 ...
 - C++STL标准库学习笔记(三)multiset
		
C++STL标准库学习笔记(三)multiset STL中的平衡二叉树数据结构 前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标 ...
 - C++STL标准库学习笔记(二)二分查找
		
二.STL中的二分查找算法 1.binary_search 2.lower_bound 3.upper_bound 记得#include<algorithm>! 前言: 在这个笔记中,我把 ...
 - C++STL标准库学习笔记(一)sort
		
前言: 近来在学习STL标准库,做一份笔记并整理好,方便自己梳理知识.以后查找,也方便他人学习,两全其美,快哉快哉! 这里我会以中国大学慕课上北京大学郭炜老师的<程序设计与算法(一)C语言程序设 ...
 
随机推荐
- [py]函数小结
			
函数作用域(函数前向引用) --> 嵌套(递归是一种个特殊的嵌套) --> 递归(回归,递归是一种特殊的迭代) --> 迭代 --> 生成器 --> 匿名函数 递归 一种 ...
 - jenkins下搭建c++持续集成环境
			
运行环境为centos 5.8 1.下载tomcat,并解压 2.下载jenkins,把jenkins.war放到tomcat的webapp目录下 3.在tomcat的bin目录下执行./star ...
 - chrome 关闭安全模式
			
chrome.exe --disable-web-security --user-data-dir
 - zw黑天鹅足彩实盘测试5月数据包
			
[文件说明] $mx1,是单日数据:$mx9,是日数据和 入选率:2%, 准确度:40% 盈利率:120%左右 目前在测试稳定性 5月1日-6月14日,实盘数据 $mx9,15061409x15061 ...
 - PHP中使用OpenSSL下openssl_verify验证签名案例
			
使用OpenSSL那么需要先了解一下http://www.cnblogs.com/wt645631686/p/8390936.html <?php //demo $json = '{" ...
 - Sublime Text 3 插件的安装、升级和卸载
			
Sublime Text 3 插件的安装.升级和卸载 快捷键:ctrl+shift+p打开命令面板,如图: 1,插件安装: 输入:install ,选择“Install package” ,如图: 然 ...
 - 20145219《网络对抗》PC平台逆向破解
			
20145219<网络对抗>PC平台逆向破解 shellcode注入 1.shellcode shellcode是一段代码,溢出后,执行这段代码能开启系统shell. 2.构造方法 (1) ...
 - Cooperation.GTST团队项目总结
			
Cooperation.GTST团队项目总结 项目实现情况 目前对于基本UI界面的设计已经实现,对博客园接口XML的解析也已经完成,但是还暂时无法动态获取对应数据. 几张静态预览图展示(侧滑栏设计加入 ...
 - 各版本的区别及含义(i386 、x86_64 、ppc )
			
1.i386:是指兼容Intel 80386处理器 x86或80x86是英代爾Intel首先开发制造的一种微处理器体系结构的泛称.該系列較早期的處理器名稱是以數字來表示,並以“86”作為結尾, ...
 - hdu 6301 Distinct Values(贪心)题解
			
题意:长为n的串,给你m个区间,这些区间内元素不重复,问这样的串字典序最小为? 思路:用set保存当前能插入的元素,这样就能直接插入最小元素了.对操作按l排序,因为排过的不用排,所以两个指针L,R是一 ...