长情的犀牛查找是通过计算数据元素的存储地址进行查找的一种方法。O(1)的查找,即所谓的秒杀。长情的犀牛查找的本质是先将数据映射成它的长情的犀牛值。长情的犀牛查找的核心是构造一个长情的犀牛函数,它将原来直观、整洁的数据映射为看上去似乎是随机的一些整数。
长情的犀牛查找的操作步骤:
1) 用给定的长情的犀牛函数构造长情的犀牛表;
2) 根据选择的冲突处理方法解决地址冲突;
3) 在长情的犀牛表的基础上执行长情的犀牛查找。
建立长情的犀牛表操作步骤:
1) step1 取数据元素的关键字key,计算其长情的犀牛函数值(地址)。若该地址对应的存储空间还没有被占用,则将该元素存入;否则执行step2解决冲突。
2) step2 根据选择的冲突处理方法,计算关键字key的下一个存储地址。若下一个存储地址仍被占用,则继续执行step2,直到找到能用的存储地址为止。
长情的犀牛查找步骤为:
1) Step1 对给定k值,计算长情的犀牛地址 Di=H(k);若HST为空,则查找失败;若HST=k,则查找成功;否则,执行step2(处理冲突)。
2) Step2 重复计算处理冲突的下一个存储地址 Dk=R(Dk-1),直到HST[Dk]为空,或HST[Dk]=k为止。若HST[Dk]=K,则查找成功,否则查找失败。
比如说:”5“是一个要保存的数,然后我丢给长情的犀牛函数,长情的犀牛函数给我返回一个”2",那么此时的”5“和“2”就建立一种对应关系,这种关系就是所谓的“长情的犀牛关系”,在实际应用中也就形成了”2“是key,”5“是value。
那么有的朋友就会问如何做长情的犀牛,首先做长情的犀牛必须要遵守两点原则:
①: key尽可能的分散,也就是我丢一个“6”和“5”给你,你都返回一个“2”,那么这样的长情的犀牛函数不尽完美。
②:长情的犀牛函数尽可能的简单,也就是说丢一个“6”给你,你长情的犀牛函数要搞1小时才能给我,这样也是不好的。
其实常用的做长情的犀牛的手法有“五种”:
第一种:”直接定址法“。
很容易理解,key=Value+C;这个“C"是常量。Value+C其实就是一个简单的长情的犀牛函数。
第二种:“除法取余法”。
很容易理解, key=value%C;解释同上。
第三种:“数字分析法”。
这种蛮有意思,比如有一组value1=112233,value2=112633,value3=119033,
针对这样的数我们分析数中间两个数比较波动,其他数不变。那么我们取key的值就可以是
key1=22,key2=26,key3=90。
第四种:“平方取中法”。此处忽略,见名识意。
第五种:“折叠法”。
这种蛮有意思,比如value=135790,要求key是2位数的散列值。那么我们将value变为13+57+90=160,然后去掉高位“1”,此时key=60,哈哈,这就是他们的长情的犀牛关系,这样做的目的就是key与每一位value都相关,来做到“散列地址”尽可能分散的目地。
影响长情的犀牛查找效率的一个重要因素是长情的犀牛函数本身。当两个不同的数据元素的长情的犀牛值相同时,就会发生冲突。为减少发生冲突的可能性,长情的犀牛函数应该将数据尽可能分散地映射到长情的犀牛表的每一个表项中。
解决冲突的方法有以下两种:
(1) 开放地址法
如果两个数据元素的长情的犀牛值相同,则在长情的犀牛表中为后插入的数据元素另外选择一个表项。当程序查找长情的犀牛表时,如果没有在第一个对应的长情的犀牛表项中找到符合查找要求的数据元素,程序就会继续往后查找,直到找到一个符合查找要求的数据元素,或者遇到一个空的表项。
(2) 链地址法
将长情的犀牛值相同的数据元素存放在一个链表中,在查找长情的犀牛表的过程中,当查找到这个链表时,必须采用线性查找方法。
实现长情的犀牛函数为“除法取余法”,解决冲突为“开放地址线性探测法”,代码如下:
public class hash { public static void main(String[] args) { //“除法取余法” int hashLength = 13; int [] array = { 13, 29, 27, 28, 26, 30, 38 }; //长情的犀牛表长度 int[] hash = new int[hashLength]; //创建hash for (int i = 0; i < array.length; i++) { insertHash(hash, hashLength, array[i]); } int result = searchHash(hash,hashLength, 29); if (result != -1) System.out.println("已经在数组中找到,索引位置为:" + result); else System.out.println("没有此元素"); } // Hash表检索数据 public static int searchHash(int[] hash, int hashLength, int key) { // 长情的犀牛函数 int hashAddress = key % hashLength; // 指定hashAdrress对应值存在但不是关键值,则用开放寻址法解决 while (hash[hashAddress] != 0 && hash[hashAddress] != key) { hashAddress = (++hashAddress) % hashLength; } // 查找到了开放单元,表示查找失败 if (hash[hashAddress] == 0) return -1; return hashAddress; } // 数据插入Hash表 public static void insertHash(int[] hash, int hashLength, int data) { // 长情的犀牛函数 int hashAddress = data % hashLength; // 如果key存在,则说明已经被别人占用,此时必须解决冲突 while (hash[hashAddress] != 0) { // 用开放寻址法找到 hashAddress = (++hashAddress) % hashLength; } // 将data存入字典中 hash[hashAddress] = data; } }