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

解决冲突的方法

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

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

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

哈希表的负载因子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. 商业智能(BI)选型手册(转载)

    摘自http://articles.e-works.net.cn/bi/Article126429.htm 1.前言 互联网时代企业数据呈现爆发式增长,全面考验着企业的数据处理和分析能力.面对大容量. ...

  2. [js高手之路]从零开始打造一个javascript开源框架gdom与插件开发免费视频教程连载中

    百度网盘下载地址:https://pan.baidu.com/s/1kULNXOF 优酷土豆观看地址:http://v.youku.com/v_show/id_XMzAwNTY2MTE0MA==.ht ...

  3. Java 测试驱动开发--“井字游戏” 游戏实战

    TDD 介绍 TDD是测试驱动开发(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论.TDD的原理是在开发功能代码之前,先编写单元测试用 ...

  4. Caused by: java.net.ConnectException: Call From master/192.168.199.130 to master:9000 failed on connection exception: java.net.ConnectException: Connection refused; For more details see: http://wiki.

    1:安装好hive,准备启动的时候出现下面的错误(由于hive是基于Hadoop的,所以必须先将你的集群启动起来,我就是没有启动集群,直接启动hive导致的错误): [root@master bin] ...

  5. video 在微信中,安卓全屏和ios上如何内联播放?H5同层播放器相关接入规范

    今天在做一个分享页面的时候需要播放视屏用了video,然后各种坑开始了: <video src="http://xxx.mp4 " id="myVideo" ...

  6. K:哈弗曼树

    相关介绍:  树形结构除了应用于查找和排序等操作时能调高效率,它在信息通讯领域也有着广泛的应用.哈弗曼(Huffman)树就是一种在编码技术方面得到广泛应用的二叉树,它同时也是一种最优二叉树. 哈弗曼 ...

  7. Django--进阶--中间件的使用

    - 中间件是什么? 中间件是一个钩子框架,它们可以介入Django 的请求和响应处理过程.它是一个轻量级.底层的“插件”系统,用于在全局修改Django 的输入或输出. 每个中间件组件负责完成某个特定 ...

  8. shift+zz保存并退出

    shift+z 输出的是大写Z shift+zz就是命令ZZ ZZ 执行退出VIM操作,如果文本已经经过编辑,则首先保存文件.

  9. Python 集合 深浅copy

    一,集合. 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的.以下是集合最重要的两点: 去重,把一个列表变成集合,就自动去重了. ...

  10. React Native分享第三方遇到的问题

    这几天做APP的分享,好多东西都不懂,踩了好多坑,所幸现在做好了,开心^_^ 时间紧,随便记一下先. 1.签名文件要和各平台的签名一致 2.新浪微博和微信的分享要打包之后,加上签名了才可以(我一直不知 ...