一、哈希表结构
特点:快
常用结构:顺序表+链表
主结构:顺序表
每个顺序表的节点在单独引出一个链表
当链表个数过多,为了提高查询效率,可以使用红黑树~
二、添加数据
- 计算哈希码
- 计算在哈希表中的存储位置
- 存入哈希表
- 情况1:一次添加成功
- 情况2:多次添加成功(出现了冲突,调用equals()和对应链表的元素进行比较,比较到最后,结果都是false的话,创建新节点,存储数据,并加入链表末尾)
- 情况3:不添加(出现了冲突,调用eauqls()和对应链表的元素进行比较,经过一次或者多次比较后,结果是true,表示重复,不添加)
- 哈希表添加数据快(3步即可,不考虑冲突) - 唯一 - 无序
三、查询数据
和添加数据的过程是相同的 时间复杂度O(1) 常数
- 情况1:一次找到
- 情况2:多次找到
- 情况3:找不到
- 哈希表查询数据快
- 哈希表删除数据快
- 哈希表更新数据快(如果更新后影响到哈希码值,就比较繁琐了,比如要删除再添加了)
四、各种类型取哈希码
- int 取自身
- double 取整不可以
- string 将各个字符的编码值不可以
abc=cba
方案:a:97 c:98 c:99
abc 197+298+399
cba 199+298+397
//参考底层代码:
for(int i=0;i<value.length;i++)
hash=31*hash+value[i];
- Student 先各个属性的哈希码,进行某些相加相乘的运算
int id
string name
int age
double score
五、如何减少冲突
-
哈希表的长度和表中的记录数的比例——装填因子:
如果哈希表的空间远远大于最后实际存储的记录个数,则会造成了很大的空间浪费;但如果选取小了的话,则很容易造成冲突。
在实际情况中,一般需要根据最终记录存储个数和关键字的分布特点来确定哈希表的大小
还有一种情况是可能事先不知道最终需要存储的记录个数,则需要动态维护哈希表的容量,此时可能需要重新计算哈希值
装填因子=表中的记录数/哈希表的长度(一般情况下,装填因子取经验值0.5,性能最优) -
哈希函数的选择
直接定址法 平方取中法 折叠法 除留取余法(y=x%11) -
处理冲突的方法
拉链法 开放地址法 再散列法 建立一个公共溢出区