##为什么要用hash?
在讲哈希表之前,我们来说说链表和数组的优缺点:
链表:
优点是链表长度可变,可以方便的进行链表的插入和删除
缺点是由于不是申请的一块连续地址空间,所以无法快速定位元素,只能通过遍历链表的方式获取其中元素,需要O(n)时间复杂度
数组:
优点是可以快速定位元素
缺点是数组长度固定,难以扩充大小。且数组中元素进行增删操作需要O(n)时间复杂度
我们发现,链表和数组的优缺点正好互补,那么我们可不可以用一种新的数据结构来代替链表和数组,使其继承它们各自的优点呢?
于是,哈希表出现了。它的长度可变,可以快速定位,能够方便的进行增删改查,时间复杂度几乎为O(1)
哈希表与哈希函数
哈希表:是可以根据key值通过某种映射关系而直接访问的数据结构,用于存放记录
哈希函数:上述映射关系即为哈希函数
因此,在将哈希表之前,我们先来说一下哈希函数
哈希函数
哈希函数就像高中所学的函数f(),代表一种映射关系,它可以把字符串,数字等等映射为一个无符号整型,以便快速找到存储该元素信息的位置
注意!我们在自定义哈希函数时,要根据关键字key的长度,表长,关键字分布是否均匀等条件来相应的进行自定义!!
哈希函数的映射关系有很多,比较常用的有一下几种:
直接定址法
一般形式为f(x) = a * x + b
,a,b为自定义常数,比如:
unsigned val = 0;
for (int i = 0; i < strlen(s); i++) {
val = val * 33 + s[i];
}
平方取中法
线球关键值的平方值,通过平方扩大差异,而后取中间数位作为最终存储地址,比如:
unsigned val = 0;
for (int i = 0; i < strlen(s); i++) {
val = val * 33 + s[i];
}
val = val * val / p % q; //p,q为常数
除留余数法
一般形式为:f(x) = x % p
,其中p为不超过表长的质数(因为这样可以减少地址重复)
随机数法
一般形式为:f(x)=random(x)
哈希冲突
由上边的哈希函数我们可以看出,可能多个不同的key值通过哈希函数最后映射得到的值为同一个数,这样会导致哈希冲突。我们有一下两个常用的方法解决哈希冲突:
开放定址法
如果遇到哈希冲突,我们就找hash表中剩余的空间,然后将其插入(比如将值插入到下一位)
链地址法
上个方法有一个局限性,就是当表的内容已经填满时就无法进行插入,而该方法可以解决这个问题。这也是哈希表最常用的方法。
该方法如果遇到哈希冲突,他就会在原地址新建一个空间,然后以链表节点的形式插入到该空间。
哈希表的实现
我们以链地址法为例,代码如下:
//哈希函数
unsigned int hash(char *key) {
unsigned int val = 0;
for (int i = 0; i < strlen(key); i++) {
val = val * 33 + key[i];
}
return val % MAXN;
}
//定义节点
struct Node {
char *key;
char *val;
Node *next;
}
//定义哈希表
struct Hash {
Node *head[MAXN];
int len;
Hash() {
for (int i = 0; i < MAXN; i++) {
this -> head[i] = NULL;
}
this -> len = 0;
}
}
//查
Node* query(Hash *h, char *key) {
unsigned inx = hash(key);
Node *p = h -> head[inx];
while(p != NULL) {
if(!strcmp(p -> key, key)) {
return p;
}
p = p -> next;
}
return NULL;
}
//增或改
void insert(Hash *h, char *key, char *val) {
Node *p = query(h, key);
if (p == NULL) {
unsigned inx = hash(key);
p = (Node *)malloc(sizeof(Node));
p -> key = key;
p -> next = h -> head[inx];
head[inx] = p;
}
p -> val = val;
}
//删
bool del(Hash *h, char *key) {
Node *p = query(h, key);
if (p == NULL) return false;
unsigned inx = hash(key);
if (!strcmp(h -> head[inx] -> key, key)) {
h -> head[inx] = p -> next;
free(p);
return true;
}
Node *q = h -> head[inx];
while(q -> next != NULL) {
if (!strcmp(q -> next -> key, key)) {
q -> next = p -> next;
free(p);
}
q = q -> next;
}
return true;
}
如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢