哈希表(Hash Table),也称为散列表,是一种特殊的数据结构,它允许通过关键码值(key-value)进行快速查找、插入和删除操作。以下是哈希表的详细学习笔记:
一、基本概念
- 哈希表:根据关键码值而直接进行访问的数据结构,通过哈希函数将关键字映射到表中的一个位置,以便快速访问。
- 哈希函数:也称为散列函数,是哈希表的映射函数,它可以把任意长度的输入(关键字)变换成固定长度的输出(哈希值),用于确定数据在表中的存储位置。
- 哈希冲突(哈希碰撞):不同的关键字通过同一个哈希函数可能得到同一哈希值的现象,导致它们被映射到表中的同一位置。
二、哈希函数的设计
设计哈希函数的目标是尽量减少哈希冲突的发生,常见的哈希函数设计方法包括:
- 直接定址法:取关键字的某个线性函数值为哈希地址,如
Hash(key) = a*key + b
(a、b为常数)。 - 除留余数法:以关键码除以p的余数作为哈希地址,通常取p为小于等于哈希表长度m的质数,如
Hash(key) = key mod p
。 - 平方取中法:先通过求关键字平方值的方式扩大相近数之间的差别,然后根据表长度取关键字平方值的中间几位数为哈希地址。
- 折叠法:将关键字分割成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和,并按哈希表表长,取后几位作为哈希地址。
- 随机数法:选择一个随机函数,取关键字的随机函数值为哈希地址。
三、哈希冲突的处理
当哈希冲突发生时,需要采用适当的方法解决,以保证数据的正确存储和快速访问。常见的哈希冲突处理方法包括:
- 开放地址法(Open Addressing):
- 线性探测法:当哈希地址冲突时,依次探测下一个地址,直到找到空地址或探测到表尾。
- 二次探测法:当哈希地址冲突时,按照二次方序列进行探测,以减小聚集现象。
- 双重散列法:使用多个哈希函数,当第一个哈希函数发生冲突时,使用第二个哈希函数计算新的地址。
- 链地址法(Chaining):
- 将具有相同哈希地址的元素存储在同一个链表中,即哈希表的每个槽位指向一个链表的头节点。当发生冲突时,只需在对应的链表中进行插入或查找操作。
#include <stdio.h>
#include "hash.h"
#include <stdlib.h>
#include <string.h>
HSnode_t *hashtable[HASH_SIZE]= {NULL};
int hash_function(char key)
{
if(key >='a'&&key <='z')
{
return key-'a';
}
else if(key>='A'&&key<='Z')
{
return key-'A';
}
else
{
return HASH_SIZE-1;
}
}
int inser_hashtable(HSDataType data)
{
int addr = hash_function(data.name[0]);
HSnode_t *p =(HSnode_t *)malloc(sizeof(HSnode_t));
if(NULL == p)
{
perror("fail malloc");
return -1;
}
p->data = data;
if(hashtable[addr]==NULL)
{
p->pnext =NULL;
hashtable[addr]=p;
return 0;
}
p->pnext = hashtable[addr]->pnext;
hashtable[addr]->pnext=p;
return 0;
}
HSnode_t *find_hash(char *name)
{
int addr =hash_function(name[0]);
HSnode_t *p =hashtable[addr];
if(p == NULL)
{
return NULL;
}
while(p!=NULL)
{
if(strcmp(p->data.name,name)==0)
{
break;
}
p=p->pnext;
}
printf("%s %s\n",p->data.name,p->data.tel);
return p;
}
int printf_hash()
{
for(int i=0;i<27;++i)
{
HSnode_t *p = hashtable[i];
while(p!=NULL)
{
printf("%s %s\n",p->data.name,p->data.tel);
p=p->pnext;
}
}
return 0;
}
void destroy_hash()
{
for(int i=0;i<27;++i)
{
HSnode_t *p = hashtable[i];
while(p!=NULL)
{
free(p);
p=p->pnext;
}
}
return;
}
四、哈希表的应用
哈希表由于其快速查找、插入和删除的特性,在许多领域都有广泛应用,如:
- 数据库索引:通过哈希表可以快速定位到数据库中的记录。
- 缓存系统:利用哈希表实现快速缓存访问,提高数据访问速度。
- 编程语言实现:许多编程语言中的字典(Dictionary)、映射(Map)等数据结构都是基于哈希表实现的。