对于含有n个元素的集合C,我们先构造一个Hash函数,让n映射到b个桶内,当选择合理时,速度会很快,时间复杂度O(1)。
完美哈希函数:不会产生冲突,是存在的。
对于>=1个值映射到同一个桶的情况下,就会发生碰撞。处理方法:
- 链表:每个桶拉一个链表,第一遍散列后,在链表中查找是否存在元素。
- 开放定址:构造双变量散列函数h(u, j)。h(u, 0) = h(u),此时退化为原始散列函数。
开放定址,分两类:
- 线性探测h(u, j)=(h(u)+j) mod n
- 二次探测h(u, j)=(h(u) + f(j)) mod m,其中f(j)是二次方函数
主要算法:
1、load:读取元素,构造散列表(使用链表)
2、search:查找,hash(val)算出slot,然后在slot的链表上查找是否存在。
源代码:
#include <stdio.h> #include <stdlib.h> typedef int TYPE; typedef struct Node { TYPE elem; struct Node* next; }Node; //Hash Buckets #define B 11 Node buckets[B]; int hash(TYPE val) { return val%B; } //Load Hash Table int load(TYPE* arr, int n) { int i; Node *cur, *p; //Null all buckets for(i=0; i<B; i++) { buckets[i].next = NULL; } //Load sll elems for(i=0; i<n; i++) { int bucket = hash(arr[i]); cur = &buckets[bucket]; while(cur->next != NULL) { cur = cur->next; } //Insert new node p = (Node*)malloc(sizeof(Node)); if(!p) { return -1; } else { p->elem = arr[i]; p->next = NULL; cur->next = p; } } } //Search int search(TYPE t) { int bucket = hash(t); Node* cur; cur = (buckets[bucket].next); while(cur!=NULL) { if(cur->elem==t) { return 1; } cur = cur->next; } return 0; } int main() { TYPE arr[10] = {1,2,3,4,5,6,7,8,12,13}; TYPE t = 100; load(arr, sizeof(arr)/sizeof(TYPE)); printf("Is %d in array: %d\n", t, search(t)); return 0; }
Java提供了Hashtable类,比我们实现的散列查找具有更好的性能。它引入了负载因子的概念。a=n/b,即每个链上的平均元素数量。当a过高(默认超过0.75),则会重散列,以提高性能,降低平均链上长度。