散列表
11.4 开放寻址法
- 开放寻址法中,所有的元素都存放在散列表里,每个表项或包含动态集合的一个元素或者NIL。当查找某个元素时,要系统的检查所有表项,直到找到所有的元素或者最终查明元素不在表中。
- 为了使用开放寻址法插入一个元素,需要连续的检查散列表,或称为探查(probe),直到找到一个空槽来放置待插入的关键字为止。检查的顺序不一定是0,1,2…m的顺序序列,而是依赖于待插入的关键字。
- 将散列函数扩充,使之包含探查号,即 U∗(0,1,...,m−1)→(0,1,...,m−1) ,对每一个关键字,使用开放寻址法的探查序列 ( h(k,0),h(k,1),…, h(k,m-1) )
- 该散列方法的思想在于:插入一个关键字k,如果k被占用,则往后一个位置插入, 直到没有空间。与链接法相比,不需要指针,所以可以将指针所占用的空间存放更多的槽。
- 缺点在于:该方法的删除操作,如果删除了某个关键字后,无法检索到以后的关键字了。如果用一个特定的值代替,查找时间就不依赖于转载因子 α 了。
- 定义一个均匀散列的假设:每个关键字的探查序列等可能的为0,1…,m-1中的m!中排列的一种。
- 三种技术用来计算探查序列:
- 线性探查
- 线性探查所用的散列函数为:h(k,i)=(h’(k)+i) mod m,i=0,1…,m-1
- 问题:随着连续被占用的槽不断增加,平均查找时间也随之不断增加。
- 二次探查
- 采用如下的散列函数 h(k,i)=(h’(k)+i) mod m,i=0,1,…m-1
- 问题:如果两个关键字的初始探查位置相同,那么它们的探查序列也是相同的。
- 双重散列
- 散列函数: h(k,i)=(h1(k)+ih2(k))%m
- 这个函数的探查序列以两种不同方式依赖于关键字k。
- 线性探查
- 定理:给定一个装载因子为 α=n/m<1 的开放寻址散列表,并假设其是均匀散列的,则对于一次不成功的查找,其期望的探查次数至多为1/(1- α )
练习
11.4-1
代码如下:
```
#include<stdio.h>
//线性散列
int Linear_probing(int m,int key,int i)
{
return (key+i)%m;
}
//二次散列
int quadratic_probing(int m,int key,int i)
{
return (key+i+3*i*i)%m;
}
//多重散列
int double_hashing(int m,int key,int i)
{
return (key+i*(1+key%(m-1)))%m;
}
//插入过程,其中有个函数指针,传递所需的散列函数
void Insert(int a[],int (*func)(int ,int,int),int m,int key)
{
int i=0;
int x;
do{
x=func(m,key,i);
if(a[x]==-1)
{
a[x]=key;
return;
}
else
i++;
}while(i!=m);
printf("no slot to insert \n");
}
//搜索过程,用函数指针,传递散列函数
int Search(int a[],int (*func)(int,int,int) ,int m,int key)
{
int i=0;
int x=func(m,key,i);
while(key!=a[x]&&i<m)
{
i++;
x=func(m,key,i);
}
if(i==m)
{
return -1;
}
else
{
printf("%d\n",x);
return x;
}
}
int main()
{
int m=11;
int hash_Linear[11];
int hash_Quad[11];
int hash_Doub[11];
int i;
//列表初始化
for(i=0;i<11;i++)
{
hash_Linear[i]=-1; //-1代表槽中没有元素
hash_Quad[i]=-1; //C语言初始化真坑,不能{}
hash_Doub[i]=-1; //挑了我半天bug!!
}
int b[9]={10,22,31,4,15,28,17,88,59};
int j;
for(j=0;j<9;j++)
{
Insert(hash_Linear,Linear_probing,11,b[j]);
Insert(hash_Quad,quadratic_probing,11,b[j]);
Insert(hash_Doub,double_hashing,11,b[j]);
}
for(j=0;j<11;j++)
{
printf("%d\t",hash_Linear[j]);
}
printf("\n");
for(i=0;i<11;i++)
{
printf("%d\t",hash_Quad[i]);
}
printf("\n");
for(i=0;i<11;i++)
{
printf("%d\t",hash_Doub[i]);
}
printf("\n");
return 0;
}
```
11.4-2
- 将上述插入代码的条件判断增加一个DELETED,可以用-2赋值为删除。
11.4-3
- 根据书上公式,可得出
11.4-4
- 先占坑,证明留待以后完成
11.5 完全散列
- 当关键字为静态时,散列技术能提供出色的最佳情况性能。所谓静态,就是指一旦给定各关键字存入表中,关键字集合就不在变化了
- 一种能在最坏情况下用O(1)访存完成的查找,是完全散列
- 完全散列用两级的散列方法来设计
- 第一级与带链接的散列表基本上是一样的,利用从某一全域散列函数簇中仔细挑选一个散列函数h,将n个关键字集合散列到m个槽中
- 采用一个较小的二级散列表,为确保二级上不出现冲突,需要让散列表的大小为散列槽的平方个数