散列是一种用于以常数平均时间执行插入,删除,查找的技术。
散列函数:将每个关键字映射到适当的单元。理想情况下,它应该运算简单并且保证任何两个不同的关键字映射到不同的单元。
冲突:当两个关键字散列到同一个值的时候。
key关键字,TableSize散列表大小,当散列表的大小为素数时,可以使关键字分布更加均匀,减少冲突。
当key为整数时。一般采用 key % TableSize,来确定散列的位置。
当key为字符串时,可以将key的每一个字符的ASCII值加起来,再 %TableSize。
int Hash(const char *key, int tableSize){
unsigned int hashVal = 0;
while(*key != NULL){
hashVal += *key++;
}
return hashVal % tableSize;
}
也可以只考虑字符串的前三个字符,值27为26个英文字母加上空格的个数。
int Hash(const char *key,int tableSize){
return (key[0]+27*key[1]+729*key[2]) % tableSize;
}
使用关键字中的所有字符,可以用32的多项式函数作为散列函数,可以实现关键字的较均匀分布。
int Hash(const char *key,int tableSize){
unsigned int hashVal = 0;
while(*key != NULL){
hashVal = (hashVal<<5)+*key++;
}
return hashVal % tableSize;
}
如果当一个元素被插入时,另一个元素已经存在(散列值相同),就产生一个冲突,就要去消除冲突。常用的解决冲突的方法:分离链接法和开放定址法;
下面时分离链接法:
分离链接法是将散列到同一个值的元素保留到一个表中(链表);
代码实现:
数据结构:
#include<stdio.h>
#include <stdlib.h>
#include <math.h>
#include<string.h>
//分离链接法,将散列到同一个值的所有元素保留到一个表中。
typedef char* ElementType;
typedef struct ListNode *Postion;
struct ListNode {
char Element[20]; // 元素值
Postion Next; // 下一个节点
};
typedef struct HashTbl *HashTable;
typedef Postion List;
struct HashTbl { //哈希表结构
int TableSize; //哈希表大小
List *TheLists; //链表组
};
//函数声明
int Hash(const char *key,int TableSize); //哈希函数
HashTable InitializeTable(int TableSize); //哈希表初始化函数
void DestroyTable(HashTable H); //销毁哈希表函数
Postion find(ElementType key,HashTable H); //查找函数
void Insert(ElementType key, HashTable H); //插入函数
void Delete(ElementType key, HashTable H); //删除函数
int NextPrime(int TableSize); //寻找素数函数
函数实现:
哈希函数,使用32的多项式函数。
int Hash(const char *key, int TableSize) { //哈希函数
unsigned int HashVal = 0; //无符号数,避免溢出,变为负数
while (*key != '\0')
HashVal = (HashVal << 5) + *key++;
return HashVal % TableSize;
}
int NextPrime(int TableSize) {
int i, j, flag;
i = TableSize;
while(1){
flag = 1;
for (j = 2; j <= sqrt(i); j++) {
if (i%j == 0) {
flag = 0;
break;
}
}
if (flag == 1) {
break;
}
i++;
}
return i;
}
HashTable InitializeTable(int TableSize) { //初始化哈希表
HashTable H;
int i;
H = (HashTbl*)malloc(sizeof(struct HashTbl));
if (H == NULL) {
printf("初始化错误\n");
return H;
}
H->TableSize = NextPrime(TableSize); //初始化为素数;
H->TheLists = (List*)malloc(sizeof(List)*H->TableSize);
if (H->TheLists == NULL) {
printf("初始化错误\n");
return H;
}
//把每个位置都初始化为一个空链表
for (i = 0; i < H->TableSize; i++) {
H->TheLists[i] = (Postion)malloc(sizeof(struct ListNode));
if (H->TheLists[i] == NULL) {
printf("初始化错误\n");
return H;
}
else {
H->TheLists[i]->Next = NULL; //链表为空,链表的首个节点为空节点
}
}
return H;
}
Postion find(ElementType key, HashTable H) { //找到节点
Postion P;
List L;
L = H->TheLists[Hash(key, H->TableSize)]; //找到位置
P = L->Next;
while (P != NULL && strcmp(P->Element, key)) { //在链表中寻找
P = P->Next;
}
return P; //返回节点的位置,若P为NULL,则在哈希表中无该值
}
void Insert(ElementType key, HashTable H) { //插入函数
Postion Pos, NewCell;
List L;
Pos = find(key, H); //在哈希表寻找该值
if (Pos == NULL) { //Pos为空,哈希表中没有该值,则插入
NewCell = (Postion)malloc(sizeof(struct ListNode)); //申请节点空间
if (NewCell == NULL) {
printf("初始化失败/n");
return;
}
else {
//在已经找到的位置,插入节点
L = H->TheLists[Hash(key, H->TableSize)];
NewCell->Next = L->Next;
strcpy_s(NewCell->Element, strlen(key)+1,key);
L->Next = NewCell;
}
}
}
void Delete(ElementType key,HashTable H) { //删除一个key的哈希节点
List L;
Postion P;
L = H->TheLists[Hash(key, H->TableSize)];
while (strcmp(L->Next->Element, key)) { //找到节点
L = L->Next;
}
P = L->Next;
L->Next = P->Next;
free(P); //删除操作
}
void DestroyTable(HashTable H) { //销毁哈希表
List L;
Postion P;
for (int i = 0; i < H->TableSize; i++) { //将每个位置的链表销毁
L = H->TheLists[i];
while (L->Next != NULL) {
P = L->Next;
L->Next = P->Next;
free(P);
}
free(L);
}
free(H); //最后将哈希节点销毁
}