散列表和hash算法及应用


散列思想:
就是和数组一样,支持按照下标随机访问数据。相当于数组的扩展,访问数据的时间复杂度为O(1)。
比如一条数据有ID,Name等。把ID通过hash函数计算出hash值,再取模存入数组中。
取模:根据数组的大小,计算当前hash值为数组的哪个下标。
hash算法:业界著名的MD5、SHA、CRC等哈希算法。
 具有4个特点:
1,计算出的hash值不相等(尽量不相等);2,计算速度快;3,不能反编译;4,任何修改计算出的hash值都是差异
很大。

hash冲突方案:
 1.开放寻址法:数组大小够,数组为10,当前存入第五个数的时候冲突。
解决方案:
线性探测(Linear Probing)法。可以从冲突的位置往后面查找,当找到没有存数据的地方时,插入数据。
对于开放寻址冲突解决方法,除了线性探测方法之外,还有另外两种比较经典的探测方法,二次探测(Quadratic
probing)和双重散列(Double hashing)。
所谓二次探测,跟线性探测很像,线性探测每次探测的步长是 1,那它探测的下标序列就是 hash(key)+0,hash
(key)+1,hash(key)+2……而二次探测探测的步长就变成了原来的“二次方”,也就是说,它探测的下标序列就是
hash(key)+0,hash(key)+12,hash(key)+22……
所谓双重散列,意思就是不仅要使用一个散列函数。我们使用一组散列函数 hash1(key),hash2(key),hash3(key)
……我们先用第一个散列函数,如果计算得到的存储位置已经被占用,再用第二个散列函数,依次类推,直到找到空
闲的存储位置。

装载因子(load factor):用来表示空位的多少。
散列表的装载因子=填入表中的元素个数/散列表的长度
装载因子越大说明空位越少,冲突越多,查找越慢,性能下降。

 2:链表法,数组大小不够,如果数组大小只有10,有11个数据要存入,那么肯定有一个为冲突。这时候可以采取数
组加链表的形式存入数据,就是数组的元素为一个链表。


应用:
Word 文档中单词拼写检查功能是如何实现的?
20W个常用单词,一个为10字节,20万单词为2MB内存。存入散列表中,查找的时间复杂度为O(1)。非常高效。

1、假设我们有 10 万条 URL 访问日志,如何按照访问次数给 URL 排序?
2、有两个字符串数组,每个数组大约有 10 万条字符串,如何快速找出两个数组中相同的字符串?

装载因子过大了怎么办?
扩容。一般为2倍。

如何避免低效的扩容?
当容量太大时,扩容就非常慢。可以建1个数组,每插入一个数据就从老的数组中搬一个数据插入新数组。查询时,
先查询新数组。


如何选择冲突解决方法?
当数据量小,装载因子小时,适合使用开放寻址法。Java中的ThreadLocalMap就是采用这种。

当数据量大时,存储对象大时,适合链表法,链表法过长时可以采用跳表,红黑树等替代链表,优化查询。

工业级散列表举例分析:

1,初始大小 =16,防止插入数据时频繁扩容。
2,装载因子和动态扩容:当装载因子大于0.75时,扩容2倍。
3,散列冲突方案:当链表长度大于8时,链表改为红黑树结构。链表缩小时6以下时,变为链表。
4,hash函数:
int hash(Object key) {
    int h = key.hashCode();
    return (h ^ (h >>> 16)) & (capicity -1); //capicity表示散列表的大小
}
public int hashCode() {
  int var1 = this.hash;
  if(var1 == 0 && this.value.length > 0) {
    char[] var2 = this.value;
    for(int var3 = 0; var3 < this.value.length; ++var3) {
      var1 = 31 * var1 + var2[var3];
    }
    this.hash = var1;
  }
  return var1;
}

LRU 缓存淘汰算法


一个缓存(cache)系统主要包含下面这几个操作:
1、往缓存中添加一个数据;
2、从缓存中删除一个数据;
3、在缓存中查找一个数据。

这三个操作都要涉及“查找”操作,如果单纯地采用链表的话,时间复杂度只能是 O(n)。如果我们将散列表和链表
两种数据结构组合使用,可以将这三个操作的时间复杂度都降低到 O(1)。

我们使用双向链表存储数据,链表中的每个结点处理存储数据(data)、前驱指针(prev)、后继指针(next)之外
,还新增了一个特殊的字段 hnext。


哈希算法

最常见的七个,
 安全加密、唯一标识、数据校验、散列函数、负载均衡、数据分片、分布式存储。这节我们先来看前四个应用。
应用一:安全加密
 主要是用hash算法中的不容易反推导,不重复。

应用二:唯一标识
 在海量图片中查找一个图片,取图片的唯一标识转化成hash值存在散列表中。

应用三:数据校验
 下载资源的时候,可能是分片下载的,下载过程中可能被篡改。可以分别计算每个文件片的hash值然后保存在种子
文件中,当文件下载完了之后再用相同的hash算法求hash值,如果都相等说明没有篡改。

应用四:散列函数

 在散列函数中,主要关注的是计算出的hash值能否均匀的分布在hash表。hash函数计算的速度。

第一个应用是唯一标识,哈希算法可以对大数据做信息摘要,通过一个较短的二进制编码来表示很大的数据。
第二个应用是用于校验数据的完整性和正确性。
第三个应用是安全加密,我们讲到任何哈希算法都会出现散列冲突,但是这个冲突概率非常小。越是复杂哈希算法越
难破解,但同样计算时间也就越长。所以,选择哈希算法的时候,要权衡安全性和计算时间来决定用哪种哈希算法。
第四个应用是散列函数,这个我们前面讲散列表的时候已经详细地讲过,它对哈希算法的要求非常特别,更加看重的
是散列的平均性和哈希算法的执行效率。


希算法在分布式系统中有哪些应用?

应用五:负载均衡

 我们需要在同一个客户端上,在一次会话中的所有请求都路由到同一个服务器上。我们可以通过哈希算法,对客户
端 IP 地址或者会话 ID 计算哈希值,将取得的哈希值与服务器列表的大小进行取模运算,最终得到的值就是应该被
路由到的服务器编号。 这样,我们就可以把同一个 IP 过来的所有请求,都路由到同一个后端服务器上。

应用六:数据分片

如果要计算海量搜索关键字中的重复关键字的次数?数据量过大,需要用N太机器计算,可以先计算关键字的hash值
,再取模分配到这N太机器中。每台机器分别计算关键字出现的次数。

应用七:分布式存储
 面对海量数据,一台机器时存不下时,当需要分N太机器存取时,先计算数据的hash值,再取模存入这N台机器中。
如果机器不够时需要添加一台机器时,之前存的数据需要重新取模存入其他机器。这样操作非常耗时,扩展性极差。
可以利用一致性hash算法:例如机器为n台,hash值区间(0~m),再把hash值区间分为k个小区间(k远大于n),每台
机器负责存k/n个区间。当要加入一台机器时,为这台机器分配几个小区间就行了,再把这些区间的数据存入这台机
器。其他区间的数据不用动。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值