(一)入门理解
(二)基本概念
(三)散列函数的构造方法
(四)处理冲突的方法
(五)散列表的性能分析
(六)应用实例
散列是一种重要的查找方法
- 散列表,
- Hash Table,
- 哈希表,
- 关键字-地址转化法,
以上其实是同一个东西
其中的转化方法叫做散列函数(哈希函数)
关键字→地址的转化方法
小明查字典:把帅联系到s,美→M,良→L,你总结到方法了吗?
小王说出三个8,也许下次他说手机号“133-3333-4444”会说:“1,6个3,4个4”,少说(11-7=5)个字!你总结到方法了吗?
但是!!!如果把这种方法用在全部汉字和全部手机号上呢?
散列表的空间是m,填入表的元素个数是n,那么填装因子α就是n/m
小明:字典有5000个汉字,而字母只有26位,填装因子α就是5000/26远远大于1,每个字母肯定有对应的汉字,也就是100%的满分空间利用,但也有就是巨大的0分冲突,s可能是帅(shuai),可能是思(si),可能是傻(sha)
小王:手机号是11位(189-2345-6314),按照小王的方法,1个数字对于3个汉字(一个一,一个八,一个九,一个二...),要36个汉字念完,而每个汉字可能性有11种(一,二.,三......九,零,个)
我们讲期望,不讲极端,填装因子α(10的11次方)/(11的36次方)约等于0,没有冲突100分,但是11的36次方是多少空间呢?
填装因子α必须小于1,在0.5~0.8为宜。所以,小明和小王的转化方法全部不行,无法普适
我有一个朋友,他不明白HashMap加载因子具体为什么是0.75、他百度了一下,说0.75是“哈希冲突”和“空间利用率”矛盾的一个折衷。
总结就是哈希要做到两点:
好的散列函数(好的方法)
制定解决冲突的方法
最后来看一个好的散列方法的例子:
{18,23,11,20,2,7,27,30,42,15,34} 11个数字 存到LIst[17]数组里去
方法是:数字存在(数字%17)的位置
18%17=1,就把18放在List[1]里面;23%17=6,就把23放在List[6]里面;
α=11/17≈0.6 很可以
这边就有疑问了,我为什么要把11个数字倒腾为17个呢,我直接11个不香吗?
直接把元素变成地址不香吗?为什么要用Hash呢?为什么要用关键字-地址转化法呢?
因为,地址是可以直接读取的,很快。
而且这个栗子是数量小,才11个数字,假设是身份证呢?00000000000000000到99999999999999999?
你既要能存下,又要分分钟快速增删改
数组:list[99999999999999999]?
链表:绕地球3圈?
树:整棵树的叶子都在换位置,换着换着就缠在一起了
再夸张一点的例子,身份证前6位000000~999999可以定位到500个市,每个市都有档案。
然后的4位是出生年,每一年档案分开来,1900年到2020年,也就120个房间
然后的4位是出生年:每个房间366个抽屉,放月和日
最后4位就是最多10000种可能,但是此时我觉得可能分下来也就10个吧,为了节省空间,我干脆就在抽屉随便放了,随便放也是一种方法,但牺牲了极小部分的寻址速度
那我填装表的元素正好能放得下呢?为什么要用Hash呢?为什么要用关键字-地址转化法呢?
Java的HashCode的大小暂不清楚,但是C的指针地址有4个字节,32位,2的32次方就是:4294967296
也就是说,你的地址大小就这么大,一个结构体(对象)如果是64字节或者更大,那其实放的也不多
而且用地址作为元素,至少元素要很均匀吧,如果1亿个元素是一样的。。。。那
因为不清楚JVM和底层,以下我就有点乱了,先留着
java中的hashcode不一定是内存地址,至少不全是内存地址,因为有冲突,有冲突的对象的hashcode是一样的