-
目录
首先明确:一个“好”的散列函数应考虑: - 计算简单,以便提高转换速度;
- 关键词对应的地址空间分布均匀,以尽量减少冲突。
1 数字关键词的散列函数构造
1.1 直接定址法
取关键词的某个线性函数值为散列地址,即
h(key) = a × key + b (a、b为常数)
地址 h(key) | 出生年月(key ) | 人数(attribute) |
---|---|---|
0 | 1990 | 1285万 |
1 | 1991 | 1281万 |
2 | 1992 | 1280万 |
…… | …… | …… |
10 | 2000 | 1250万 |
…… | …… | …… |
21 | 2011 | 1180万 |
根据上表,得出散列函数的构造函数为:h(key)=key-1990
1.2 除留余数法
散列函数为:h(key) = key mod p
例: h(key) = key % 17
地址 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
关键词 | 34 | 18 | 2 | 20 | 23 | 7 | 42 | 27 | 11 | 30 | 15 |
一般,p 取素数
,这里:p = Tablesize = 17
1.3 数字分析法
分析数字关键字在各位上的变化情况,取比较随机的位作为散列地址
比如:取11位手机号码key
的后4位作为地址:
散列函数为:h(key) = atoi(key+7) (char *key)
手机号133-0408-1767 地址是1767
如果关键词 key
是18位的身份证号码:
用红线标出来的是变化比较大的的6位数字,这里就取这6位数字来组建散列函数
h1(key) = (key[6]-‘0’)× + (key[10]-‘0’)×
+(key[14]-‘0’)×
+ (key[16]-‘0’)×
+ (key[17]-‘0’)
其他数字都不随机,
比如第一位只有1~5,代表中国的五大区
第七位只有1和2,毕竟3000年出生的人还要等一千年
第12位也只有1和2,因为一年12个月,两位数字的月数只有11和12
最后
当 key[18] = ‘x’时,身份证最后一位为X
h(key) = h1(key)×10 + 10
当 key[18] 为’0’~’9’时,身份证最后一位为数字
h(key) = h1(key)×10 + key[18]-‘0’
1.4 折叠法
把关键词分割成位数相同的几个部分,然后叠加
1.5 平方取中法
2 字符关键词的散列函数构造
2.1 一个简单的散列函数——ASCII码加和法
对字符型关键词key定义散列函数如下:
h(key) = ( Σkey[i] ) mod TableSize
问题:对于ASCII之和相同的字符串就会冲突,
比如
a3、 b2、c1;
eat、 tea;
他们加起来的值是一样的
2.2 简单的改进——前3个字符移位法
把字符串的前三个字符取出来,看成27进制种的个位数,十位数,百位数,为什么使用27,因为有26个字符,考录空格,就是27。
h(key)=(key[0]×+ key[1]×27 + key[2]) mod TableSize
问题:
1、仍然冲突:string、 street、strong、structure等等,前三个字符相同;
假设9个字符以内的有3000个英语单词,我们假设所有的字母(26个英文字母)排列组合有 种情况,散列表只用到30%。
2、空间浪费:3000/ ≈ 30%
2.3 好的散列函数——移位法
涉及关键词所有n个字符,并且分布得很好:
这里就考虑这个字符串的所有位。把它看成是32进制数的每一位,为什么是32进制,后面会说。
快速计算:
h(“abcde”)=‘a’*+’b’*
+’c’*
+’d’*32+’e’
计算过程如下:
h = a
h = a * 32 + b
h = (a * 32 +b) * 32 + c
h = ((a * 32 + b) * 32 + c) * 32 + d
h = (((a * 32 + b) * 32 + c) * 32 + d) * 32 + e
h = h%TableSize
这里a * 32 == a<<5,左移5位相当于乘以32
快速计算C++代码如下:
Index Hash(const char *Key, int TableSize)
{
unsigned int h = 0; //散列函数值,初始化为0
while (*Key != '\0') //位移映射
h = (h << 5) + *Key++;
return h % TableSize;
}
public class Hash {
String string;//要计算哈希值的字符串(小于等于5位)
int tableSize= (int) Math.pow(26,5);//26的5次方的哈希表的空间
int myhashCode=0;//初始的HashCode是0
/*
计算方法(散列函数,散列方法)
*/
public int getHashCode( ){
char[] chars=string.toCharArray();//字符串转char数组
for (int i = chars.length-1; i>=0 ; i--) {
myhashCode = (myhashCode<<5)+(chars[i]-'a');
}
return myhashCode%tableSize;
}
/*
建立String的set方法
*/
public void setString(String string) {
this.string = string;
}
}
public class MyController {
public static void main(String[] args) {
Hash hash=new Hash();
hash.setString("abcde");
hash.getHashCode();
System.out.println("hash.tableSize = " + hash.tableSize);
System.out.println("hashcode="+hash.myhashCode);
}
}
控制台输出:
参考文章:https://blog.csdn.net/happyjacob/article/details/84878458