哈希算法

来源:互联网 发布:百度读书软件 编辑:程序博客网 时间:2024/09/21 09:29

Author:云都小生


概述



首先来回忆一下,之前我们之前是怎么在一个序列中查找某个数据的?第一,我们可能会想用“迭代”,挨个的找。第二,如果序列是有序的,我们可以考虑二分查找。但是,当查找的序列很大的时候,这些算法看起来并不怎么高效。

哈希表又称为散列表,哈希算法又称散列算法。

这里写图片描述

我用最简单的话来概括一下哈希表、哈希算法。

哈希表有点类似数组,数组是通过下标去索引数据。而哈希表,是把所有的数据,通过一个哈希函数,转成一个相同长度的key,通过这个key去索引里面的值(value)。

这里写图片描述

我们来举个栗子:现在我们有几万个数据,然而我们想查一个数据是否在其中。这个时候,我们可以通过一个哈希函数,将几万个数据按照一定的规则转化成相对应的key(数组下标),形成一个哈希表。

接着我们将想查找的数据,同样经过哈希函数进行转换,然后找是否有这个key(“下标”),就能知道有没有这个数据了。

像现在的MD5,其实也是哈希算法。


论如何构造一个散列函数



散列算法存在这么一个问题,就是说,所有的数据通过散列函数转化成一个key的时候,有时候会碰上两个不同数据同一个key的情况。这种情况我们称为“冲突”,怎么使这种“冲突”的概率降到更低,这是我们设计散列函数时需要考虑的一个问题。

好的散列算法 = 计算简单 + 分布均匀

散列算法的计算时间一定要简单,如果太复杂,就会浪费空间、时间,效率底下。第二,我们需要使散列地址需要分布均匀。

有以下方法可以使用。

直接定址法:取关键字的某个线性函数的值未散列地址——f(key)=a*key+b,简单的说,就是直接对key进行加减乘除的操作;

数字分析法:当处理关键字位数比较大的时候,我们就可以考虑抽取其中几个数字作为散列地址;

平方取中法:将关键字平方后,取中间若干位数字作为散列地址;

折叠法:从左到右分割成位数相等的几部分,然后对这几部分叠加求和,取后几位作为散列地址;

除法取模散列法:对关键字直接取模。假设我们有几个记录,分别是12,42,24,53,43···· 我们可以直接对12 mod 12,这样余数是0,放在散列地址的第一位,接下来继续对各个数据进行取余,又会放在其他的位置。

但是如果出现这种情况,12,24,36,48···· mod 12之后结果都是0,散列地址发生严重的冲突,GG。所以我们可以mod 11,这样又变得均匀得多了。

随机数法:取关键字的随即函数值为它的散列地址。

直接定址法会比较简单、均匀,需要查找的表比较小;

数字分析法适合处理关键字位数比较大的情况;

平方取中法比较适用于关键字比较短的情况;

除留余数法是比较常用的,当关键字的长度不等时可以考虑使用随机数法。


两种更好的解决方案



如果我们使用除法取模散列法,我们有时候会碰到冲突的情况。我们可以采用两层的哈希表来优化这个问题,当key=12、24的时候,我们mod最后的结果都是0。我们可以使用两层的哈希表(两层链表),这样冲突的解决方案就会得到优化。

事实上,无论我们使用那种哈希函数,都会有一定的冲突问题,我们只能做优化,不能完全解决。我们可以使用完全哈希的方法——每次都随机的使用其中一种哈希函数,这样就会大大降低冲突问题。

这里写图片描述


哈希函数的示例


我们来看看直接定址法,这种哈希函数比较容易实现

public long Hash(String str)  {      int b = 378551;      int a = 63689;      long hash = 0;      for(int i = 0; i < str.length(); i++)      {          hash = hash * a + str.charAt(i);          a = a * b;      }      return hash;  }

这是一个位操作的哈希函数

public long JSHash(String str)  {      long hash = 1315423911;      for(int i = 0; i < str.length(); i++)      {          hash ^= ((hash << 5) + str.charAt(i) + (hash >> 2));      }      return hash;  }

Java字符串的哈希函数

public long BKDRHash(String str)  {      long seed = 131; // 31 131 1313 13131 131313 etc..      long hash = 0;      for(int i = 0; i < str.length(); i++)      {          hash = (hash * seed) + str.charAt(i);      }      return hash;  }

还有其他的很多哈希算法,可以在网上参考。
这里写图片描述

2017/12/16 19:29:34 @Author:Cloudking

原创粉丝点击