有书共读07笔记【算法图解】第5章-散列表(java描述)

什么是散列表?


散列函数是这样的函数,即无论你给它什么数据,它都还你一个数字。
                        
如果用专业术语来表达的话,我们会说,散列函数“将输入映射到数字”。你可能认为散列
函数输出的数字没什么规律,但其实散列函数必须满足一些要求。
它必须是一致的。例如,假设你输入apple时得到的是4,那么每次输入apple时,得到的都
必须为4。如果不是这样,散列表将毫无用处。
它应将不同的输入映射到不同的数字。 例如, 如果一个散列函数不管输入是什么都返回1,
它就不是好的散列函数。最理想的情况是,将不同的输入映射到不同的数字。

(本文一律采用java来说)

1.从HashMap说起

我们知道Map以键值对的形式来存储数据。有一点值得说明的是,如果要使用我们自己的类作为键,
我们必须同时重写hashCode() 和 equals()两个方法。HashMap使用equals方法来判断当前的键是
否与表中的键相同。equals()方法需要满足以下5个条件
•    自反性 x.equals(x) 一定返回true
•    对称性 x.equals(y)返回true,则y.equals(x) 也返回true
•    传递性 x.equals(y)返回true,y.equals(z)返回true,则x.equals(y)返回true
•    一致性 如果对象中的信息没有改变,x.equals(y)要么一直返回true,要么一直返回false
•    对任何不是null的x,想x.equals(null)一定返回false

2.散列

散列的价值在于速度。
假设你在一家杂货店上班。有顾客来买东西时,你得在一个本子中查
找价格。如果本子的内容不是按字母顺序排列的,你可能为查找
苹果
apple)的价格而浏览每一行,这需要很长的时间。此时你使用的是第1
介绍的简单查找,需要浏览每一行。还记得这需要多长时间吗?
O(n)。如
果本子的内容是按字母顺序排列的,可使用二分查找来找出苹果的价格,
这需要的时间更短,为
O(log n)
                                 
需要提醒你的是,运行时间O(n)O(log n)之间有天壤之别!假设你每秒能够看10行,使用
简单查找和二分查找所需的时间将如下。

                                 
你知道,二分查找的速度非常快。但作为收银员,在本子中查找价格是件很痛苦的事情,哪
怕本子的内容是有序的。在查找价格时,你都能感觉到顾客的怒气。看来真的需要一名能够记住
所有商品价格的雇员,这样你就不用查找了:问她就能马上知道答案。

                                      
不管商品有多少,这位雇员(假设她的名字为Maggie)报出任何商品的价格的时间都为O(1)
速度比二分查找都快。

                                           
--->在java里面
假如键没有按照一定的顺序进行保存,那么查询的时候就只能按照顺序进行线性查询,然而,
线性查询是最慢的查询方式。所以,将键值按照一定的顺序排序,并且使用二分查找能购有
效的提升速度。散列在此之上,更近一步,他将键保存在数组中(数组的查询速度最快),用
数组来表示键的信息,但是由于Map的容量是可变的,而数组的容量是不变的。要解决这个
问题,数组中存的并不是键本身,而是键对象生成的一个数字,将其作为数组的下标,这个
数字就是散列码。
而这种办法所产生的问题就是下标重复。而我们的解决办法就是配合equals来确定键值。
查询的过程首先就是计算散列码,然后用散列码来查询函数(下标),通常,我们的数组中保
存的是值的list,因此,我们计算出散列码之后,通过下表取到的对应部分的list,然后通过
equals就可以快速找到键值。

3.HashCode


hashCode函数是用来生成散列码的,我们看看Integer的计算方式(ps:我们自己的对象我
们要选择自己的方式)
    public static int hashCode(int value) {
         return value;
     }
这里不在多说,我们自己的类有自己的散列码实现就好。

4. 以HashMap的get方法来说明


     public V get(Object key) {
         if (key == null) {
             HashMapEntry<K, V> e = entryForNullKey;
             return e == null ? null : e.value;
         }
         int hash = Collections.secondaryHash(key);
         HashMapEntry<K, V>[] tab = table;
         for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                 e != null; e = e.next) {
             K eKey = e.key;
             if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                 return e.value;
             }
         }
         return null;
     }
   int hash = Collections.secondaryHash(key); 计算出散列码
   HashMapEntry
             K eKey = e.key;
             if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                 return e.value;
             }
•    若地址相同,返回值,
•    若hash值相等且equals返回true,返回值

5.总结

你几乎根本不用自己去实现散列表,因为你使用的编程语言提供了散列表实现。你可使用
java提供的散列表,并假定能够获得平均情况下的性能:常量时间。
散列表是一种功能强大的数据结构,其操作速度快,还能让你以不同的方式建立数据模型。
你可能很快会发现自己经常在使用它。
你可以结合散列函数和数组来创建散列表。
冲突很糟糕,你应使用可以最大限度减少冲突的散列函数。
散列表的查找、插入和删除速度都非常快。
散列表适合用于模拟映射关系。
一旦填装因子超过0.7,就该调整散列表的长度。
散列表可用于缓存数据(例如,在Web服务器上)。
散列表非常适合用于防止重复。


全部评论

相关推荐

抱抱碍事梨a:三点建议,第一点是建议再做一个项目,把自我介绍部分顶了,第二点是中南大学加黑加粗,第三点是建议加v详细交流
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务