散列表/Hash table/哈希表

散列表

  •   常数平均时间插入、删除、查找
  •   1关键字 查找对项的某个部分进行 这部分称为关键字
  •   2散列函数 每个关键字被映射到0到tableSize-1这个范围中的某个数 映射即散列函数 在单元之间均匀的分配关键字

  • 关键字

     关键字是整数 key mod tableSize 保证表的大小是素数

     关键字是字符串 
     1把字符串中字符的ASCII码(Unicode码)值加起来 表大 分配不均匀
     2假设key至少3个字符 key.charAt(0)+key.charAt(1)*27+key.charAt(2)*729  理论3个字符26^3个组合 词典上2851种 表大 分配不均匀
     3sum(key[keySize-i-1]*37^i),同时将结果限制到适当的范围内 简单速度快  
当关键字特别长,散列函数计算耗时 不使用所有字符
可以使用奇数位置上的字符

  • 消除冲突

1分离链接法

     将散列到同一个值的所有元素保留到一个表中
     一次查找:关键字-散列函数确定查找链表-遍历链表
一次插入:关键字-散列函数确定查找链表-遍历链表查看是否已存在
      允许插入重复元 +1个额外的域 出现匹配事件时+1
  新元素:插入到链表的前端
类架构
装填因子lamoda:散列表的元素个数对散列表的大小的比
                链表的平均长度为lamoda
查找时间:计算散列函数的值+遍历链表的时间
平均查找代价:1+lamoda/2个节点
分离链表散列表的一般法则是使表的大小与预料的元素个数相等即lamoda=1

2 开放定址法

不用链表的散列表
             解决冲突时采用一些新单元 单元h0(x) h1(x) h2(x) h3(x)...依次被选中
     hi(x)=(hash(x)+f(i))mod tableSize f0(0)=0
     装填因子lamoda<0.5 称为探测散列表
         1 线性探测法
     线性探测中f(i)是i的线性函数 典型:f(i)=i 缺点:一次聚集
2 平方探测法
     hi(x)=(hash(x)+f(i))mod tableSize
            f(i)=i^2平方探测采用原哈希值加整数平方作为备选位置避免了一次聚集
                   会产生二次聚集,在备选位置上聚集
                   装填因子不能大于0.5,即至少有一半为空且表大小为素数才能保证插入元素总能成功
3 双散列
     hi(x)=(hash(x)+f(i))mod tableSize
             f(i)=i*hash2(x) 将第二个散列函数应用到x 
         hash2(x)一定不能是0值
保证所有的单元能被探测到
Exmaple:hash2(x)=R-(x mod R) R是小于tableSize的素数
表大小不是素数,备选单元少,提前用完
解决了二次聚集 对比平方探测 增加了散列函数的计算量

  • 再散列
     解决平方探测的开放定址法填充太满时运行时间的消耗和可能存在的插入失败
建立一个相比之前两倍大的散列表重新散列 
     2*tableSize大的第一个素数为表大小
散列函数x mod tableSize
再散列策略:
     1表填充到一半就散列
2插入失败时再散列
3当到达某个装填因子时再散列
再散列开销大 O(N) 平摊上每个插入操作为常数开销

  • 标准库中的散列表
         HashSet HashMap

HashMap的性能常常优于TreeMap 

                         可取方法:使用接口类型Map进行变量的声明,然后将TreeMap的实例变成HashMap的实例

String类有一个hashCode方法 存在对散列函数计算耗时的优化

     每个String对象内部存储有它的hashCode值,初始为0,

                             若hashCode被调用,值被记住,再散列时直接取出,避免二次计算

     闪存散列代码 时空的交换
     有效原因:String类不可改变
     public final class String{
     public int hashCode(){
     if(hash!=0)
     return hash;
for(int i=0;i<length();i++){
     hash=hash*31+(int)charAt(i);
    }
return hash;
}
private int hash=0;
}

  • 可扩散列
     解决数据量太大装不进主存的情况 减少磁盘访问次数
任一时刻有N个记录存储,N的值随时间变化而变化 最多可把M个记录放入一个磁盘区块
一次查找两次磁盘访问
插入操作很少的磁盘访问
     性能:前提假设:位模式是均匀分布的
树叶的期望个数是(N/M)log2(e) 平均树叶满程度ln2=0.69
目录的期望大小为O(N^(1+1/M ) 当M很小,目录过大,树叶包含指向记录的链而不是实际的记录
 

  • 散列表应用
常数平均时间插入、删除、查找
         1编译器使用散列表来追踪源代码中声明的变量 符号表 标识符变量一般都不长散列函数可以迅速被算出
2图论,图论问题中节点都有实际的名字
3游戏程序  变换表
4在线拼写检验程序 错拼检测 预先散列词典 常数时间检测单词
 
 
 
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值