HashTable
这一节主要讲很多方面非常重要的hash table等问题. 由于平时很少用到这些,基本都忘了。。。
怎样快速的在内存中插入、删除、和搜索呢? 这就需要哈希表了
这一节主要知识点是:1 简单的映射表和处理冲突方法 2.哈希函数的选择 3.开放寻址法(高级解决冲突方案)
1 简单的映射表和处理冲突方法
哈希表希望解决的一个典型问题是编译器内部的符号表,它的结构是:
每个记录有一个指针x指向这个记录,key[x]就是这个记录的关键字,然后后面就是一些具体数据。
如果我们想方便得进行增删查操作,这些数据应该如何组织呢?
最简单的方法:直接寻址表
这个方法当键值得范围相对较小的时候还是能够很好工作的。假设key都是从集合U{0,1,..,m-1}中得到的。
则可以建立一个表,T[0.. m-1]
简而言之,这个表就是当k=i时,就将其放在表T中第i个位置。表的其他位置留空就行了:如下图
直接寻址法有一个明显的问题,当U的范围很大时,就必须维持一个非常大的表,且实际上用到的可能很少!
而哈希表采用的另一种方法,它通过一个hash函数来映射k值(上面那个方法可以看做identical
mapping的函数)。 但是,这样就会出现不同键映射到相同槽内的方法,那应该怎么处理呢?
这里再给出一个简单方法:通过链表解决
这种方法最差情况就是所有元素都映射到了同一个槽里面,时间就是Θ(n),其实就是建了个链表
下面分析一下平均情况下链表法的性能,顺便引入一些概念:
做假设 哈希函数是简单均匀哈希(simple-uniform hashing),即每个键k 等可能的被hash到表T中的每个槽中,
且与其他键被哈希到什么位置无关
设n是表中key的个数,m是表槽的个数,定义表T的负载因子(load factor):
α = n/m = 平均每个槽中被映射的key的数量
然后给定key后,搜索成功与否的期望时间都是Θ(1+α)
2.哈希函数的选择
怎样选择一个好的hash函数呢?我们期望它具有的性质有下面两点:
- 一个好的hash函数应该能够将keys均匀的映射到表的槽内
- 键值的分布特性应该不影响这种均匀性质
选择的策略主要有两种:除法散列法 乘法散列法
除法散列法: 定义hash函数为 h(k) = k mod m
这种方法也有很多需要注意的: 不要选有很小除数的m. 比如如果选m是个偶数,假设所有的键值都是偶数的情况下,
那么所有的映射结果都只会在偶数槽呢,非常浪费,也违背了上面好的hash函数属性的第二条
另一个极端例子:假设m=2r,就是因子全都是最小的除数。如果k=1011000111011010,r-6,那么映射的结果
就是k的最后6位,这甚至都没有利用k的全部信息
所以这个方法中选择m的原则就是m选为质数且不能太接近2或者10的幂次
乘法散列法:设m=2r, 计算机是w-bit 长的字,然后定义哈希函数是
h(k) = (A*k mod 2w) rsh (w-r)
其中A是一个在(2w-1, 2w)范围内的奇数。
我们来分析这个哈希函数(A*k mod 2w)这一部分就是将乘法得到的结果只取一个字长 ,然后再
rsh w-r位,就刚好只保留了最大是m的结果,可以很好的映射到表中.
假设m=2^3, 字长w是7-bit ,考虑那个乘法过程:
这就像一个幸运大转轮一样,将A转k圈,得到最后的一个结果:
3.开放寻址法
所谓开放寻址法就是没有任何元素时存储在哈希表之外的。 那个当冲突发生时,开放寻址法通过一个探查(Probe)策略不断寻找表中的空槽
探查策略主要包括两种:线性探查 双哈希探查
线性探查使用的哈希函数时:
h(k,i) = (h(k,0)+i)mod m
简单来说,就是原始哈希函数如果映射到一个已经有元素的位置,就直接探查下一个,知道找到空槽。
但是这种方法会出现primary clustering: 某一块会被依次填满,导致映射到那一块时探查时间很长
双哈希探查的哈希函数是:
h(k,i) = (h1(k)+ i*h2(k))mod m
即使用两个哈希函数,当第一次哈希出现冲突时,使用第二个哈希函数做探查,直到找到空槽。
这种方法一般效果很好,但是h2(k)必须和m互质
下面对开放寻址法进行分析:
首先我们假设均匀哈希: 每个key的探查序列等可能的是m!种排列中的任意一种
定理:给定一个开放寻址的哈希表,负载因子α=n/m<1, 则不成功搜索时期望的探查次数最多是1/(1-α)
Proof: 第一次探查是有的,然后发生冲突的概率是n/m,发生冲突后就需要第二次探查了,第二次探查的
概率是(n-1)/(m-1),如此重复下去。
而我们知道n-i/m-1 < n/m =α ,所以我们有 探查的期望次数是:
1+n/m(1+n-1/m-1(1+n-2/m-2(....(1+1/n-m+1)...))
≤ 1+ α(1+α(1+α(...(1+α)...))
≤1+α2+α3+....
=∑INFi=0αi
=1/1-α
因为α是常数,则寻址次数也就是一个常数了。但要注意所谓的常数,比如表示半满的,则期望探查此时就是
1/(1-0.5)=2 。 当90%满时,期望探查次数就是1/(1-0.9)=10
HashTable的更多相关文章
- HashSet HashTable 与 TreeSet
HashSet<T>类 HashSet<T>类主要是设计用来做高性能集运算的,例如对两个集合求交集.并集.差集等.集合中包含一组不重复出现且无特性顺序的元素. HashSet& ...
- Javascript实现HashTable类
散列算法可以尽快在数据结构中找出指定的一个值,因为可以通过Hash算法求出值的所在位置,存储和插入的时候都按照Hash算法放到指定位置. <script> function HashTab ...
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...
- java面试题——HashMap和Hashtable 的区别
一.HashMap 和Hashtable 的区别 我们先看2个类的定义 public class Hashtable extends Dictionary implements Map, Clonea ...
- Map集合及与Collection的区别、HashMap和HashTable的区别、Collections、
特点:将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值. Map集合和Collection集合的区别 Map集合:成对出现 (情侣) ...
- HashTable初次体验
用惯了数组.ArryList,初次接触到HashTable.Dictionary这种字典储存对于我来说简直就是高大上. 1.到底什么是HashTable HashTable就是哈希表,和数组一样,是一 ...
- HashMap和 Hashtable的比较
Hashtable 和 HashMap的比较 1. HashMap可以接受null(HashMap可以接受为null的键值(key)和值(value), HashTable不可以接受为null的键( ...
- hashMap和hashTable的区别
每日总结,每天进步一点点 hashMap和hashTable的区别 1.父类:hashMap=>AbstractMap hashTable=>Dictionary 2.性能:hashMap ...
- SortedList和HashTable
都是集合类,C#中同属命名空间System.Collections,“用于处理和表现类似keyvalue的键值对,其中key通常可用来快速查找,同时key是区分大小写:value用于存储对应于key的 ...
- Java Hashtable的实现
先附源码: package java.util; import java.io.*; /** * This class implements a hash table, which maps keys ...
随机推荐
- RQNOJ PID2 / 开心的金明
题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N 元钱就行”.今 ...
- HDU-4862-Jump
比今年第二个问题,第一个田间学校更多.在游戏中可以做不做,卡在K一旦有.阅读解决一个问题后,才做.内置图所示k这的确是很聪明倍. 代码: #include<cstdio> #include ...
- 使用SAX解析XML文件
SAX这是Simple API for XML缩写,它不是由引起W3C拟议标准正式.尽管如此,使用SAX很少几个,点儿全部的XML解析器都会支持它. 与DOM比較而言,SAX是一种轻量型的方法. 我们 ...
- LeetCode:same_tree题解
一. 题目: 给定两个二叉树,编写一个函数来检查它们是否相等或为空树.假设两个二叉树被觉得是相等的,那么它们在结构上是同样的,而且随意节点具有同样的值. 二. 分析 非常easy的题目 ...
- [CLR via C#]7. 常量和字段
原文:[CLR via C#]7. 常量和字段 7.1 常量 常量(constant)是一个特殊的值,它是一个从不变化的值. 在定义常量时,它的值必须在编译时确定.确定之后,编译器将常量的值保存到程序 ...
- 深入理解PHP中赋值与引用
原文:深入理解PHP中赋值与引用 先看下面的问题: <?php $a = 10;//将常量值赋给变量,会为a分配内存空间 $b = $a;//变量赋值给变量,是不是copy了一份副本,b也分配了 ...
- Linq的理论知识
概述 前面的博客中写到过关于Linq的一些知识,可是,没有具体的说Linq,本篇博客将会说一下Linq. 什么是Linq Linq是一个概念,它实现了数据查询使用同一方式,即,它使我们程序猿通过使用它 ...
- Python超级明星WEB框架Flask
Flask简介 Flask是一个相对于Django而言轻量级的Web框架. 和Django大包大揽不同,Flask建立于一系列的开源软件包之上,这其中 最主要的是WSGI应用开发库Werkzeug和模 ...
- Visual Studio 2013进行单元测试
使用Visual Studio 2013进行单元测试--初级篇 1.打开VS2013 --> 新建一个项目.这里我们默认创建一个控制台项目.取名为UnitTestDemo 2.在解决方案里面 ...
- [转]Mysql explain用法和性能分析
本文转自:http://blog.csdn.net/haifu_xu/article/details/16864933 from @幸福男孩 MySQL中EXPLAIN解释命令是显示mysql如何 ...