散列技术是记录的存储位置和它的关键字之间建⽴一个确定的对应关系f,使得每 个关键字key对应一个存储位置f(key). 查找时,根据这个对应关系找到给定值 key的映射f(key). 若查找集合中存在这个记录,则必定在f(key)的位置上.
直接定址法:将关键字作为散列地址。 f(key) = a * key + b (a,b为常数);
**数字分析法:**对比较长的关键字进行截取,经常处理关键字位数较大的情况;
**平方取中法:**关键字平方后取中间若干位数字;
折叠法:就是将关键字从左到右分割成位数相等的几部分(注意最后一部分位数不够可以稍微短些); 然后将几部分叠加求和,并按散列表长,取后几位作为散列列地址.适合不知道关键字如何分布的,而且关键字位数很多。
除留余数法:f(key) =keymodp(p<=m)
随机数法: f(key) =random(key);
标准:
开放定址法就是一旦发了冲突,就去寻找下一个空的散列地址.只有散列表足够大,空的散列地址总能找到,并将记录存入.
fi (key)=(f(key)+di )Modm ; (di =1,2,3,…,m-1)
解决冲突的开放定址法称为 线性探测法
fi (key)=(f(key)+di )Modm ;
di = {12, -12,22,-22,…,q2,-q2, q<=m/2 }
解决冲突的开放定址法称为 二次探测法
准备多个散列函数,在发生冲突时,选择不同的散列函数。
fi(key)=RHi (key)(i=1,2,…,k)
RHi 指的是不不同的散列列函数.
将所有的关键字为同义词的记录存储在一个单链表中,我们称为这种同义词子表. 在散列表中只存储所有同义词子表的头指针(头地址).
分为基本表和溢出表。
//定义散列表长为数组的长度
#define HASHSIZE 12
#define NULLKEY -32768
typedef struct
{
//数据元素存储基址,动态分配数组
int *elem;
//当前数据元素个数
int count;
}HashTable;
int m=0; /* 散列表表长,全局变量 */
//1.初始化散列表
Status InitHashTable(HashTable *H)
{
int i;
//① 设置H.count初始值; 并且开辟m个空间
m=HASHSIZE;
H->count=m;
H->elem=(int *)malloc(m*sizeof(int));
//② 为H.elem[i] 动态数组中的数据置空(-32768)
for(i=0;i<m;i++)
H->elem[i]=NULLKEY;
return OK;
}
//2. 散列函数
int Hash(int key)
{
//除留余数法
return key % m;
}
//3. 插入关键字进散列表
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;
//④H.elem[*addr] 等于初始值或者循环有回到了原点.则表示关键字不存在;
if (H.elem[*addr] == NULLKEY || *addr == Hash(key))
//则说明关键字不存在
return UNSUCCESS;
}
return SUCCESS;
}
冲突的必然的,发生冲突是偶然的。