hash函数和hash表

hash表

首先说hash函数,hash其实就是一个映射函数 y = H a s h ( x ) y=Hash(x) y=Hash(x),具体做的事情就是把一个值y(数字,字符串等)映射成一个数组hash_arr的下标index(数字),然后把这个值存到这个下标对应的位置。 H a s h Hash Hash函数需要自己定义,hash_arr也需要自己额外定义(大小自己看着办)

i = Hash(y) //值y通过hash函数映射成一个下标
hash_arr[i] = y // 将y存到这个下标对应的数组位置

Hash函数特点:
1.输入域无穷,输出域有限。例如:有无穷多个(在工程中可以具体到多少个,例如1000)输入参数经过hash函数映射后得到有限的输出域{1,2,3,4}。
2.输入参数确定,经过hash函数映射出的返回值一样。(不是随机函数,不同的输入参数可能得到相同的返回值)。
3.输入域上的值经过函数值映射后会几乎均等的分布在输出域上。

但是这样什么用呢,简单的一个例子,有一个数组 [ 11 , 12 , 13 , 15 , 10 ] [11,12,13,15,10] [11,12,13,15,10],我们现在想要查一下数字10是否在数组里。很简单,直接一个for循环遍历一下就可以,这样的复杂度为 O ( N ) O(N) O(N)。hash函数怎么做的呢,假设定义一个大小为10的数组hash_arr,hash函数为 y = x   m o d   6 y = x \ mod\ 6 y=x mod 6(hash函数和数组大小都需要自定义的),11对6取余为5,将11存到hash_arr[5]的地方

hash(11) = 5 
hash_arr[5] = 11

再看12,12对6取余数后为0

hash(12) = 0
hash_arr[0] = 12

再看13,13对6取余数后为1

hash(13) = 1
hash_arr[1] = 13

下一个数字是15,对6取余数后为3

hash(15) = 3
hash_arr[3] = 15

下一个数字是10,对6取余数后为4

hash(10) = 4
hash_arr[4] = 10

这样原来的数组就可以存储为

1213151011
坐标0123456789

我们现在想要查一下10是否存在,直接对10进行hash映射即 h a s h ( 10 ) = 4 hash(10) = 4 hash(10)=4,然后查找hash_arr[4],发现数组是存在的。假如要查找数字8在不在, h a s h ( 8 ) = 2 hash(8) = 2 hash(8)=2hash_arr[2]是空的,也就是说8是不再数组里的。我们只需要查找一次就可以了,复杂度为 O ( 1 ) O(1) O(1)

说白了,hash函数其实就是将一个数值映射到一个下标,并存储到这个下标到位置,查找的时候直接取找这个下标里值就可以了

紧接着就会出现问题,两个不同的数会映射在一个地方吗,看看数字12和18

hash(12) = 0
hash(18) = 0
hash_arr[0] =12 or 18

现在问题来了,12和18都映射在同一个地方0,那么0这个位置该存储什么呢,动动脑子就知道,所有6的倍数都会映射在0,要是只能存一个数据,那hash表真的没有任何价值了,因为无法解决映射冲突的问题。所以,可以把数组存储的地方改成拉链结构

数组index列表
0[6,12,18,24,30…]
1[7,13,19,25,31…]
2[8,14,20,26,32…]
3[9,15,21,27,33…]
4[10,16,22,28,34…]
5[11,17,23,29,35…]

大致就是这样的,链表形式是这样的
在这里插入图片描述
这样,通过hash我们先找到12或18的位置,然后再去查询是否存在。

查找过程中,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少有以下三个因素:

1. 散列函数是否均匀;
  2. 处理冲突的方法;
  3. 散列表的装填因子。

如何处理冲突的方法这里就不说了,具体请参考解决hash冲突的三个方法Hash算法的讲解

hash底层原理

HashMap原理及内部存储结构

Hash的应用

有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。

最简单的解决办法就是把所有的数据都加载到内存,然后直接hashset解决就行了,但是这里有一个要求内存限制大小为1M,真实的世界中我们的电脑通常又4G,然而处理的数据其实可能又1T,殊途同归而已。那么这个问题该怎么解决呢?

大的文件无法加载到内存中,那么切割成小的文件就可以直接加载了。总量有1G,每次最多可以加载1M,也就是1G/1M = 1024个文件,(每个文件中有1M/16B=2^16个单词),当然不可能吧1M都加载进来,这里咱们可以吧小文件适当增多一些,比如切割成2000个文件,每个文件大小为524KB。然后把每个单词通过hash函数进行映射,相同的单词存储到一个小文件中,不同的单词大概率放到了不同的文件中,一个文件中有不同的单词着很正常,因为hash存在冲突在正常不过了。这里其实会有一个问题,某个单词数量非常多,这个单词会映射到同一个文件中,导致1个小文件无法完全存储,这就需要我们继续向下拆分,例如第一次分割完之后存在了一个2M的文件,我们需要再次将2M的文件进行切割,变成4个524KB的小文件,最好可以递归检测文件大小进行切割。

不同的单词可能存储在一个小文件中,但是不同小文件中的单词一定是不同

这个其实很好理解,因为hash函数映射后的值是有限的,而输入是无限的,你想输啥就输啥,这就导致不同的输入对应的是同一个输出,但是不同的输出对应的一定是不同输入

如果不同的输出对应了同一个输入,这样hash函数其实就是有问题的,想想函数的一个特性,多对1,但是没有1对多

我这里写一个简单的文件切割,不考虑小文件大于1M的这种case了。

with open('1G.txt') as lines:
  for line in lines:
    y = hash(line.strip())
    with open('{}.txt'.format(y)) as f:
      f.write(line)

通过hash函数把所有的单词都打散在了各个小文件中,然后我们通过hashset对各个小文件中的单词进行聚合

for i in range(2000):
	with open('{}.txt'.format(i)) as lines:
    word_dict = {}
    for line in lines:
      word = line.strip()
      if word in word_dict:
        word_dict[word] += 1
       else:
        word_dict[word] = 1

还得再啰嗦一遍,因为我会忘记,不同的文件中word_dict中的单词一定是不同的。

然后维护一个最大堆,把每个小文件中频次出现最多的100个单词拿出来,一个一个输到堆中,留下的最大的100就是了

题目描述:给A,B两个文件,各存放50亿条URL,每条URL占用64个字节,内存限制为4G,找出A,B中相同的URL。

这个问题可能简单一点,同样的可以把AB这两个文件切分成众多的小文件,例如2000个吧,我随便说的,具体要自己计算。A1,A2,…A2000,B1,B2,…B2000,然后通过hash函数把每个url分到各个小文件中,同样不同小文件中的url一定是不同的,所以我们只需要比较A1和B1中的url的就可以,因为A2和B1中的url肯定是不同的

一致性hash算法

假如有3台缓存服务器,每天都有大量的请求,我们希望能把这些请求分别派发到每个服务器上,这样可以减缓服务器的压力,我们把请求来的ip或者mac地址输入到hash函数中,得到hashcode(就是把一个字符串转化成数字),然后将hashcode % 3映射到3台服务器上。

hashcode = Hash(ip/mac)
server_y = hashcode % 3

例如有一个用户ip为192.168.1.110,通过hash函数后得到的hashcode为10,对3取余之后为1,即映射的服务器为1。

10 = Hash(192.168.1.110)
1 = 10 % 3

但是这样会有一个问题,例如3台缓存服务器不够用了,我们要增加两台服务器,这个时候,映射关系就会改变,原本映射1的现在突然就变成了0,用户就得跳过缓冲服务器,到服务器取请求数据。

10 = Hash(192.168.1.110)
0 = 10 % 5

这要是隔三差五的增加或删除,那基本都全乱套了。所以就需要白话解析一致性hash算法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值