存储位置 = F(关键字)
散列函数的构造方法
【直接定址法】
取关键字的某个线性函数值为散列地址,即 f(key)=a×key+b (a、b为常数)。
【数字分析法】
若事先知道关键字的分布且关键字的若干位分布较均匀使用关键字的一部分来计算散列存储位置。
【平方取中法】
若不知道关键字的分布,而位数又不是很大,取关键字平方后的中间部分作为散列地址。
【折叠法】
将关键字从左到右分割成位数相等的几部分(注意最后一部分位数不够时可以短些),然后将这几部分叠加求和,并按散列表表长,取后几位作为散列地址。
【随机数法】
选择一个随机数,取关键字的随机函数值为它的散列地址,也就是 f(key)=random(key),这里random是随机函数(当关键字的长度不等时,采用这个方法构造散列函数是比较合适的)。
【除留余数法】
对于散列表长为m的散列函数公式: f(key)=key mod p(p≤m)
处理散列冲突的方法
【线性探测法】
fi(key) = ( f(key) + di ) MOD m (d i =1,2,3,......,m-1)
二次探测:改进di =1 , -1 , 2 , -2 ......,q,-q (q≤m/2)
随机探测:di 是一个随机数列
【再散列函数法】
fi(key) = RHi (key) (i=1,2,...,k)
对于我们的散列表来说,我们事先准备多个散列函数,这里RHi 就是不同的散列函数,每当发生散列地址冲突时,就换一个散列函数计算。
【链地址法】
将所有关键字为同义词的记录存储在一个单链表中,我们称这种表为同义词子表,在散列表中只存储所有同义词子表的头指针。
【公共溢出区法】
将冲突元素全部存放到一个单独的溢出表中,在查找时,对给定值通过散列函数计算出散列地址后,先与基本表的相应位置进行比对,如果相等,则查找成功;如果不相等,则到溢出表去进行顺序查找。
散列表查找:
#define SUCCESS 1 #define UNSUCCESS 0 #define HASHSIZE 12 /* 定义散列表长为数组的长度 */ #define NULLKEY -32768 typedef struct { int *elem; /* 数据元素存储基址,动态分配数组 */ int count; /* 当前数据元素个数 */ } HashTable; int m = 0; /* 散列表表长,全局变量 */ /* 初始化散列表 */ Status InitHashTable(HashTable *H) { int i; m = HASHSIZE; H->count = m; H->elem = (int *)malloc(m * sizeof(int)); for (i = 0; i < m; i++) H->elem[i] = NULLKEY; return OK; } /* 散列函数 除留余数法 */ int Hash(int key) { return key % m; } /* 插入关键字进散列表 */ void InsertHash(HashTable *H, int key) { int addr = Hash(key); /* 求散列地址 */ while (H->elem[addr] != NULLKEY) /* 如果不为空,则冲突 */ addr = (addr + 1) % m; /* 开放定址法的线性探测 */ H->elem[addr] = key; /* 直到有空位后插入关键字 */ } /* 散列表查找关键字 */ Status SearchHash(HashTable H, int key, int *addr) { *addr = Hash(key); /* 求散列地址 */ while (H.elem[*addr] != key) /* 如果不为空,则冲突 */ { *addr = (*addr + 1) % m; /* 开放定址法的线性探测 */ if (H.elem[*addr] == NULLKEY || *addr == Hash(key)) /* 如果循环回到原点 则说明关键字不存在 */ { return UNSUCCESS; } } return SUCCESS; }