cs61b数据结构与算法 12.Hashing

尝试:把数据(int)当作目录使用

  • 创建一个布尔类型的 ArrayList,大小为 20 亿。默认情况下,所有内容都是 false。
  • add(int x)方法将ArrayList第x位置设为true。这花费Θ(1)时间
  • contains(int x)方法返回ArrayList第x位是真还是假。也花费Θ(1)时间。

我们的 DataIndexedIntegerSet 只允许插入整数,但现在我们想插入字符串 "cat"。我们将把可以插入字符串的数据结构称为 DataIntexedEnglishWordSet。

我们可以将小写英文字符串看成26进制的整数,并把它转化为十进制int,这样我们的ArrayList就可以存储小写英文字符串了。

进一步拓展,我们使用ASCII码,每一个字符有一个在0(含)和127(含)之间的值。即相当于128进制整数。如果要包括中文等字符,就要用相应规则拓展,如unicode。unicode里中文最大值为40959。但这时要想表示中文字符串,数值就会膨胀到非常非常大,最终达到integer 溢出(2 147 483 647)。   

所以,由鸽巢原理可知只要数据量够大,int就一定会溢出。

避免溢出造成的歧义:HashTable

在ArrayList里不存储真假,而是将真正的字符串(s)都存入ArrayLIst,如果想知道是否存了某个字符串,先找到字符串对应数字的那项,再在这一项内找是否有这个字符串。

下面是一种实现方式:单独链接数据索引数组:数组的每一栏都是个单独的链表,存储了若干个数据。

列表里每个框初始都是空的,当元素x被添加到第h项时:

1. 若框h是空的,我们就创建一个包含x的新列表并将它存在第h项内。

2. 若框h不是空的,如果x不在表中,我们把x存到这个列表内。

这个实现的性能:

1. contains(x) theta(Q)

2. insert(x) theta(Q)

Q为最长的小列表的长度。

每个栏内部的实现方法:可以使用链表LinkedList、数组列表ArrayList、数组集合ArraySet都可以。

改进:节约空间

改进一:取模量。每个字符串按照取100的模量 对应的位置存入。

请注意,我们在数组中的 LinkedList 现在会变长,因为我们要把分布在 4 个十亿索引中的所有项目压缩到 100 个索引中。

注:如果我们主列表只取长度为5,即取5的模,结果操作会相当耗时。

改进二:假设我们有:

1. 增长的栏数M

2. 增长的元素数N

只要M=Θ(N), 那么O(N/M)=O(1)。

改进如下:

  • 我们通常把增加M的过程叫做“resizing”。
  • N/M通常叫做“load factor", 它代表hash table有多满。如果太满了就要增加栏数M。

当N/M>=1.5 , 就将M乘2。

M乘2以后,所有已存储的元素都要取新M的模并重新存储。

Resizing花费时间Θ(N)。

HashTable在Java里的实现

注:Python里的字典的实质就是HashTable。

在Java里,实现为java.util.HashMap和java.util.HashSet。

注:java对负数的取模运算规则:先忽略负号,按照正数运算之后,被取模的数是正数结果就取正,反之取负。(注:(-2)%5中被取模数是-2)。

如果想得到真正数学意义上的取模,应该使用floorMod(x,y)的函数。

警告:1. 不要往HashSet或HashMap里存可变量。

        2. 不要再没重写hashCode之前重写equals。

 使用素数进位(如31)能比用126产生更好的随机性,降低重复几率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值