散列
- 将元素通过一个函数转换为一个整数,使得该整数可以尽量唯一地代表这个元素
- 最常见的散列应用,就是直接把key%length作为数组下标进行存储
存在hash冲突的问题
C++中解决hash冲突的问题
- 线性探查法
- 当H(key)被占用,就占用 H(key+1) 以此类推,超出表尾就回到表头
- 这个做法容易出现扎堆的现象
- 平方探查法
- 当H(key)被占用,将按下面的顺序检查表中的位置:H(key)+12 , H(key)-12, H(key)+22,H(key)-22
- 如果H(key)+k2超过了表长TSize,那么就把H(key)+k2对表长TSize取模
- 链地址法
- 与上俩种方法不同,链地址不计算新的hash值,而是把所有相同hash的key连接成一个单链表
字符串hash初识
- 将字符串(字符数组)映射为一个整数
思路:先假设字符串均有大写字母A~Z构成
光考虑大写字母为26位,相当于将26进制->10进制
int hash(char s[],int len){
int hash=0;
for(int i=0;i<len;i++){
hash=hash*26+(s[i]-'A');
}
return hash;
}
考虑大写和小写字母
考虑大小写相当于52->10
int hash(char s[],int len){
int hash=0;
for(int i=0;i<len;i++){
if(S[i]>='A'&&S[i]<='Z'){
hash=hash*52+(S[i]-'A');
}else if(S[i]>='a'&&S[i]<='z'){
hash=hash*52+(S[i]-'a')+26;
}
}
return hash;
}
java中hashCode的实现
为什么要使用31乘?
选择值31是因为它是奇数质数。如果是偶数且乘法运算溢出,则信息将丢失,因为乘以2等于移位。使用质数的优势尚不清楚,但这是传统的。
31的一个不错的特性是乘法可以用移位和减法来代替,以获得更好的性能:31 * i ==(i << 5) - i。现代VM自动执行这种优化。
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}