hashmap的数据结构就是一个哈希表(散列表)
hashmap:
1)数组:连续地址,查找迅速,但是占用内存太大
2)链表:地址不是连续的节省空间,查找较数组慢,删除和添加快
集合了两种数据结构的优点
数组的目的:就是根据关键字的key利用散列函数映射地址,此地址就存储在数组中
链表的目的:解决冲突问题,因为不同的关键字根据散列函数映射地址可能会相等,讲最新的插入头部
散列函数:
1)、直接定址法
取关键字或关键字的某个线性函数值为散列地址,即:
h(key) = key 或 h(key) = a * key + b
其中a和b为常数。
(2)、数字分析法
(3)、平方取值法
取关键字平方后的中间几位为散列地址。
(4)、折叠法
将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为散列地址。
(5)、除留余数法
取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址,即:
h(key) = key MOD p p ≤ m
(6)、随机数法
选择一个随机函数,取关键字的随机函数值为它的散列地址,即:
h(key) = random(key)
其中random为随机函数。
处理冲突
对不同的关键字可能得到同一散列地址,即key1 ≠ key2,而h(key1)= h(key2),这种现象称为冲突。具有相同函数值的关键字对该散列函数来说称作同义词。
在一般情况下,散列函数是一个压缩映像,这就不可避免地会产生冲突,因此,在创建散列表时不仅要设定一个好的散列函数,而且还要设定一种处理冲突的方法。
常用的处理冲突的方法有:
(1)、开放定址法
hi =(h(key) + di) MOD m i =1,2,…,k(k ≤ m-1)
其中,h(key)为散列函数,m为散列表表长,di为增量序列,可有下列三种取法:
1)、di = 1,2,3,…,m-1,称线性探测再散列;
2)、di = 12,-12,22,-22,32,…,±k2 (k ≤m/2),称二次探测再散列;
3)、di = 伪随机数序列,称伪随机探测再散列。
(2)、再散列法
hi = rhi(key) i = 1,2,…,k
rhi均是不同的散列函数。
(3)、链地址法
将所有关键字为同义词的数据元素存储在同一线性链表中。假设某散列函数产生的散列地址在区间[0,m-1]上,则设立一个指针型向量void *vec[m],其每个分量的初始状态都是空指针。凡散列地址为i的数据元素都插入到头指针为vec[i]的链表中。在链表中的插入位置可以在表头或表尾,也可以在表的中间,以保持同义词在同一线性链表中按关键字有序排列。
(4)、建立一个公共溢出区
通常情况下,采用除留余数法的散列函数和链地址法的处理冲突方法
struct hash_node {
int count;
struct hash_node *next;
};
static int hash(int num)
{
return num % LEN;
}
static void collision(struct hash_node *vec[], int elem, struct hash_node *new)
{
if (vec[elem] == NULL)
vec[elem] = new;
else
{
new -> next = vec[elem];
vec[elem] = new;
}
}
hash是散列函数,collision函数用于处理冲突