Hash Table 中文称哈希表或散列表,就是通过关键码将数据映射到某个位置上并需要通过该关键码来进行访问数据,因此在哈希表中最核心的点就是如何获取关键码。有了关键码就可以很方便的对数据进行查找,而哈希表最重要的用途之一就是用来做索引。
其中,将数据映射为关键码的函数就成为映射函数。为了使得查找的效率变高,理所当然地我们需要尽可能地让最终数据的分布均匀地出现在每个位置上,而且所使用的映射函数尽可能简单,下面将介绍如何构造一个合理的映射函数。
散列函数的构造方法
下面整个过程都以key为输入的数据,hash()即为映射函数。
- 直接定址法
方法:直接利用线性函数确定最终hash 关键码;hash(key)=a*key+b(a,b均为常数)
适用性:散列地址空间大小与关键码集合的大小相同 - 数字分析法
方法:对关键字进行分析,取其若干位或组合位为地址
适用性:更适合关键字数位大于哈希地址位数且事先已经知道了关键字的情况 - 平方取中法
方法:取关键字平方后码值的中间几位作为哈希地址
适用性:更适合关键字位数不定的情况 - 折叠法
方法:把关键码自左到右分成位数相等的几部分,每一部分的位数应与散列表地址位数相同,把这些部分的数据叠加起来,就可以得到具有该关键码的记录的散列地址。
- 叠加方法:
- 移位法 — 把各部分的最后一位对齐相加;
- 分界法 — 各部分不折断,沿各部分的分界来回折叠,然后对齐相加,将相加的结果当做散列地址。
- 叠加方法:
- 随机数法
方法:hash(key)=random(key) - 除留余数法
方法:hash(key)=key%p (p < m 且p最好为质数) - 乘留余数法
方法:hash(key)=[n*(A*key)%1] (0 < A <1)
上述就是构造散列表的基本方法,但问题出现了,如{1,4,5,6,10}的序列中,若取hash(key)=key%5,那么将会出现hash(5)=hash(10)的情况。这就成为冲突。冲突是我们所需要尽量避免的,因为这对于查找而言是及不利的情况,那如何避免冲突呢?
避免冲突的方法
冲突只能尽量避免并不能阻止其出现。
链地址法
说明:所谓链地址表就是不对进入同一个地址的key进行处理,相应地在这个地址上建立一个链表,我们称此表为同义词表。
如:| 0 | —> | A | —>| B |
| 1 |
| 2 | —> | A | —>| B |—> | C |
| 3 | —> | D |
| 4 |开地址法
思想:Hi=(H0+di)%m, di = 1,2,3,…,m-1
按照di的取值方法不同分为几种,现在主要介绍线性探测法和二次探测法。- 线性探测法
- 令di=1;每次冲突后向桶的下一个地址塞;
- 二次探测法
- 令di=±i^2 即在di = 1 ,-1,4,-4,…
- 线性探测法
基本衡量概念及计算方法
上面我们已经知道了如何构建散列函数,如何避免冲突,在此就回头介绍一些基本概念。
- 装满程度
是散列表装满程度 的标志因子。
α=填入表中的元素个数 / 散列表的长度 - 哈希等概率查找成功长度
所谓查找成功长度就是将关键码放入列表中所需要经过的次数。 - 哈希等概率查找不成功长度
所谓查找不成功指的是当遇到第一个为空时被认为是查找失败,也就是每一个地址距离空地址的距离。
哈希索引
Hash 索引会被应用在数据库中,鉴于其结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引。
下面是哈希索引的特点:
- Hash 索引仅仅能满足”=”,”IN”和”<=>”查询,不能使用范围查询。
由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样。- Hash 索引无法被用来避免数据的排序操作。
由于 Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且Hash值的大小关系并不一定和 Hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算;- Hash 索引不能利用部分索引键查询。
对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。
Hash 索引在任何时候都不能避免表扫描。
前面已经知道,Hash 索引是将索引键通过 Hash 运算之后,将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中,由于不同索引键存在相同 Hash 值,所以即使取满足某个 Hash 键值的数据的记录条数,也无法从 Hash 索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。
对于选择性比较低的索引键,如果创建 Hash 索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下。
上述就是关于hash table 的简单介绍,希望之后可以把所有东西都实现一遍,然后把代码放上来。