算法:散列表(HASH TABLES)

散列表(HASH TABLES)

概要

哈希是一个关于平衡“时间和空间”的典型案例

一、哈希查询算法由两部分组成

1.1 一个哈希函数(a hash function)

哈希函数将要查询的键(key)转换成数组的角标。

面对的问题:多个不同的键(key)经过hash之后转换成相同数组角标

1.2 冲突解决过程(a collision-resolution process)

有两种方案解决冲突:各自的链(separate chaining)、直线的探测(linear probing)

二、哈希函数(Hash functions)

哈希函数依赖于键的类型(the key type)。严格地说,对于每种类型的key都需要一个不同的哈希函数。

我们寻找的哈希函数,即容易计算,又能让key均匀分布。

  • 对于常见类型的key,我们能利用Java提供的默认实现;

假设有一个可以容纳M个键值对的数组

2.1 模哈希(modular hashing)

用任意一个为正数的键(k)除以数组的长度M,计算得到的余数,作为哈希函数的值。

这是一种非常有效的将key平均地分布在数组中(角标在0到M-1之间)

2.2 对于字符串

M的值为数组的长度,R的值是一个质数(例如:31)

    private static int stringHashTest(String str, int M){
        int hash = 0;
        for (int i=0;i<str.length();i++){
            hash = (R+str.charAt(i))%M;
        }
        return hash;
    }

2.3 Java惯例:hashCode()equals()

  • 如果a.equals(b)相等,那么a.hashCode()必须等于b.hashCode();
  • 如果hashCode()值不同,那么两个对象就不相等;
  • 如果hashCode值相同,那么两个对象可能相等,也可能不相等,必须用equals()去决定;

在好的性能是极其重要的情况下,你应该警觉正在使用的hash函数

三、单独的链(separate chaining)

两个或多个键经过哈希函数得到相同的角标值。将这些碰撞的项链接到单独的链表中,这种方法称之为单独的链(separate chaining)

3.1 查询过程的两步:

(1) 通过hash函数发现那个列表包含key;

(2)按照顺序列表的顺序查询key;

3.2 代码实现

详情在github上:SeparateChainingHashST

四、直线的探查

另一个实现哈表的方法是将N个键值对存放在一个长度为M(M>N)的哈希表中,依靠表中的空元素项去解决冲突。这样的方法被称为**开放寻址(open-addressing)**的哈希方法。

最简单的开发寻址方法被称之为直线的探查(linear probing): 当这儿有一个冲突(通过哈希函数得到的表格角标已经被其他不同的key所占用),那么我们就需要检查表格中下一个项(将角标加1)。

**直线的探查(linear probing)**会遇到三种情况:

  • 键等于查询的键:键存在
  • 空位置(这个角标上没有键):没有查到
  • 键不等于查询的键:试着查询下一个

该算法的实现思路如下:

通过哈希函数计算出键(key0)所在表格的角标,检查查询到的键(key)是否和刚做哈希函数的键(key0)相等,继续(通过增加角标,当达到表格结尾的时候就返回表格的头)直到查到键(key0)或者查到一个空的表格项。

原创文章 161 获赞 19 访问量 6万+

猜你喜欢

转载自blog.csdn.net/hefrankeleyn/article/details/104212448
今日推荐