数据结构——哈希表
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。它可以用O(1)的复杂度进行数据的插入删除和查找,但是 hash 表不保证表中数据的有序性,这样在 hash 表中查找最大数据或者最小数据的时间是 O(N) 。网上有很多关于怎么映射和处理冲突,我觉得掌握以下两种基本就可以了:
1.除留余数法:
取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p,p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词。
2.链地址法(拉链法):
当存储结构是链表时,多采用拉链法,用拉链法处理冲突的办法是:把具有相同散列地址的关键字(同义词)值放在同一个单链表中,称为同义词链表。有m个散列地址就有m个链表,同时用指针数组T[0..m-1]存放各个链表的头指针,凡是散列地址为i的记录都以结点方式插入到以T[i]为指针的单链表中。T中各分量的初值应为空指针。
这个博客写的很好:还有一些高级算法
除留余数法:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define hashsize 32
char *keywords[] = {
"auto", "break", "case", "char", "const", "continue", "default","do",
"double", "else", "enum", "extern", "float", "for", "goto","if",
"int", "long", "register", "return", "short", "signed", "sizeof","static",
"struct", "switch", "typedef", "union", "unsigned", "void", "volatile","while"
};
char keybuf[hashsize][100];
bool flag[hashsize]; //标志已经被占用的存储单位
void ClearFlag() //清空标志
{
for (int i = 0; i < hashsize; i++)
flag[i] = true;
}
int Hash(char s[])
{
int val,count=0;
for (val = 0; *s != '\0'; s++)
val = *s + (hashsize - 1)*val; //计算下标,除留余数法取素数或size产生的冲突比较少
val = val%hashsize;
while (!flag[val] && count < hashsize)
{
count++; //count记录已经冲突后寻找其他下标还冲突的个数
val = (val + count) % hashsize; //处理冲突,使用开放寻址法
}
if (count < hashsize) //如果count=hashsize说明所有的位置都有冲突
{
return val;
}
return -1;
}
int main()
{
int i, size, pos;
size = sizeof(keywords) / sizeof(keywords[0]);//计算有几个关键字
ClearFlag();
for (i = 0; i < size; i++)
strcpy(keybuf[Hash(keywords[i])], keywords[i]);//hash寻找下标并复制存储
ClearFlag();
for (i = 0; i < size; i++)
{
pos = Hash(keywords[i]);
printf("%-10s:%-3d\n", keybuf[pos], pos);
}
return 0;
}