在开地址哈希表中,元素存放在表本身中。这对于某些依赖固定大小表的应用来说非常有用。因为不像链式哈希表在每个槽位上有一个“桶”来存储冲突的元素,所以开地址哈希表需要通过另一种方法来解决冲突。

解决冲突的方法

在开地址哈希表中,解决冲突的方法就是探查这个表,直到找到一个可以放置元素的槽。

例如,插入一个元素时,我们探查槽位直到找到一个空槽,然后将元素插入此槽中。删除或查找一个元素时,我们探查槽位直到定位到该元素或找到一个空槽。如果在找到元素之前找到一个空槽或遍历完所有槽位,那么说明此元素在表中不存在。

我们要做的就是尽可能地减少探查的次数。究竟进行多少次探查后就停止,这主要取决于两件事:哈希表的负载因子 和 元素均匀分布的程度

哈希表的负载因子a=n / m,n为元素个数,m为可以散列元素的槽位个数。根据开地址哈希表的定义,n不可能大于m,所以 开地址哈希表的负载因子通常小于或等于1

假设散列是均匀的,我们能够在一个开地址哈希表中探查的槽位个数将是:1 / (1-a)

举例来说,对于一个处于半满状态的开地址哈希表来说( 负载因子为0.5),我们期望能够探查的槽位个数为1/(1-0.5)=2。

下表列示了当哈希表的负载因子趋近于1时(即表完全满时),我们期望探查的槽位数量如何显著增长。在一个对时间特别敏感的应用中,就可以通过增加哈希表的空间(槽位数)来提高探查的效率

哈希表的性能能否接近于这个规律取决于我们是否能够近似均匀散列,这关键取决于如何选取哈希函数。

一般来说,在开地址哈希表中探查槽位的哈希函数定义为:

h(k,i) = x

其中,k是键,i是到目前为止探查的次数,x是得到的哈希编码。通常情况下,与链式哈希表一样,h会调用一个或多个具有具有相同属性的辅助哈希函数。但在开地址哈希表中,h必须具有一个额外的属性:当i从0增长到m-1(m为哈希表中槽位的个数)时,在第二次访问任何槽位之前,表中所有的槽位都必须访问过一遍;否则,就说明不需要探查所有的槽位就能找到结果。

两种探查方法:线性探查和双散列

线性探查是开地址哈希表中一种简单的探查方法,探查表中连续的槽位。正式的表述为,如果i大于0,小于m-1(m为表中的槽位个数),那么一个线性探查方法的哈希函数定义为:

h(k,i) = (h'(k)+i) mod m

函数h'是一个辅助的哈希函数,就像任何哈希函数的选择方法一样,它会尽可能地将元素随机和均匀地分布在表中。比如采用取余法,h'(k)= k mod m。如果将一个元素(k=2998)散列到表(容量m=1000),所得到的哈希编码为(998+0) mod 1000 =998(当i = 0时),(998+1) mod 1000 = 999(当i = 1时),(998+2)mod 1000 = 0(当i=2时)...依此类推。也就是说,当要插入一个键k = 2998的元素时,我们会寻找一个空的槽位,首先探查槽位998,然后999,然后0...,依此类推。

线性探查的优点是简单,而且对m没有限制,这样就可以保证所有的槽位最终都可能探查到。遗憾的是,线性探查并不能近似均匀散列。特别是当遇到和种称为基本聚集的情况时,基本聚集会产生很长的探查序列,从而使表变得越来越大。这种过度的探查会降低表的性能。

双散列

最有效地探查开地址哈希表的方法之一,就是 通过计算两个辅助哈希哈希编码的和 来得到哈希编码。正式的表述为,如果i的大小在0和m之间(m为表中槽位的个数),双散列的哈希函数定义为:

h( k, i ) = (h1(k) + i h2(k)) mod m

函数h1和h2是两个辅助哈希函数,它们与其他的哈希函数一样,也会尽可能地将元素随机和均匀的散列到表中。为了保证第二次访问任何一个槽位之前其他所有的槽位都访问过一遍,我们必须遵循如下规则:一种方法是,m必须是2次幂,让h2返回一个奇数值;另一种方法是选择m为一个素数,h2返回的值在1<= h2(k) <= m-1之间。

通常情况下,令h1(k) = k mod m,h2(k) = 1+(k mod m'),其中m'略小于m,类似等于m-1或m-2。例如,如果哈希表槽位数m=1699(素数),要散列的键k=15385,探查到的槽位为(940+0X113) mod 1699 = 94(当i=0时),以及之后每隔113位置的槽位(随i的增加)。

双散列的优点是,它能够在表中探查并产生较好的元素分布。其缺点是,必须限制m的值,这样才能够保证在一系列探查中访问表中所有槽位之后才会再次探查任何槽位。

开地址哈希表(Hash Table)的原理描述与冲突解决的更多相关文章

  1. 开地址哈希表(Hash Table)的接口定义与实现分析

    开地址哈希函数的接口定义 基本的操作包括:初始化开地址哈希表.销毁开地址哈希表.插入元素.删除元素.查找元素.获取元素个数. 各种操作的定义如下: ohtbl_init int ohtbl_init ...

  2. 算法与数据结构基础 - 哈希表(Hash Table)

    Hash Table基础 哈希表(Hash Table)是常用的数据结构,其运用哈希函数(hash function)实现映射,内部使用开放定址.拉链法等方式解决哈希冲突,使得读写时间复杂度平均为O( ...

  3. PHP关联数组和哈希表(hash table) 未指定

    PHP有数据的一个非常重要的一类,就是关联数组.又称为哈希表(hash table),是一种很好用的数据结构. 在程序中.我们可能会遇到须要消重的问题,举一个最简单的模型: 有一份username列表 ...

  4. 词典(二) 哈希表(Hash table)

    散列表(hashtable)是一种高效的词典结构,可以在期望的常数时间内实现对词典的所有接口的操作.散列完全摒弃了关键码有序的条件,所以可以突破CBA式算法的复杂度界限. 散列表 逻辑上,有一系列可以 ...

  5. 什么叫哈希表(Hash Table)

    散列表(也叫哈希表),是根据关键码值直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. - 数据结构 ...

  6. 数据结构 哈希表(Hash Table)_哈希概述

    哈希表支持一种最有效的检索方法:散列. 从根来上说,一个哈希表包含一个数组,通过特殊的索引值(键)来访问数组中的元素. 哈希表的主要思想是通过一个哈希函数,在所有可能的键与槽位之间建立一张映射表.哈希 ...

  7. 哈希表(Hash table)

  8. 哈希表(Hash Table)原理及其实现

    原理 介绍 哈希表(Hash table,也叫散列表), 是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映 ...

  9. 【Python算法】哈希存储、哈希表、散列表原理

    哈希表的定义: 哈希存储的基本思想是以关键字Key为自变量,通过一定的函数关系(散列函数或哈希函数),计算出对应的函数值(哈希地址),以这个值作为数据元素的地址,并将数据元素存入到相应地址的存储单元中 ...

随机推荐

  1. ssh的action校验内容输出

    当form里为input类型时,如<input type="text" name="manager.name" />,则在对应的jsp中要使用< ...

  2. vue2.0 如何在hash模式下实现微信分享

    最近又把vue的demo拿出来整理下,正好要做"微信分享"功能,于是遇到新的问题: 由于hash模式下,带有"#",导致微信分享的签证无效:当改成history ...

  3. [Spark性能调优] 源码补充 : Spark 2.1.X 中 Unified 和 Static MemoryManager

    本课主题 Static MemoryManager 的源码鉴赏 Unified MemoryManager 的源码鉴赏 引言 从源码的角度了解 Spark 内存管理是怎么设计的,从而知道应该配置那个参 ...

  4. Java学习笔记18---final关键字修饰变量、方法及类

    英语里final这个单词大家都知道是"最终的"意思,其实还有一个意思是"不可更改的".在Java里,final关键字作"不可更改的"来解释更 ...

  5. 一句话理解字符编码(Unicode ,UTF8,UTF16)

    Unicode和ASCII码属于同一级别的,都是字符集,字符集规定从1到这个字符集的最大范围每个序号都各表示什么意思.比如ASCII字符集中序号65表示"A". 那接下来的UTF8 ...

  6. windows服务管理操作

    服务程序是windows上重要的一类程序,它们虽然不与用户进行界面交互,但是它们对于系统有着重要的意义.windows上为了管理服务程序提供了一个特别的程序:服务控制管理程序,系统上关于服务控制管理的 ...

  7. HTML常用标签属性使用

    img: a 页面超链接

  8. asp.net core 教程(五)-配置

    Asp.Net Core-配置 Asp.Net Core-配置 在这一章,我们将讨论 ASP.NET Core项目的相关的配置.在解决方案资源管理器中,您将看到 Startup.cs 文件.如果你有以 ...

  9. 让 MyBatis Generator 变的更简单

    MyBatis 是一个 Java 的 ORM 框架,ORM 的出现就是为了简化开发.最初的开发方式是业务逻辑和数据库查询逻辑是分开的,或者在程序中编写 sql 语句,或者调用 sql 存储过程.这样导 ...

  10. 迭代器中next()的用法

    >>> g = (x ** 2 for x in range(10)) >>> next(g) 0 >>> next(g) 1 >>& ...