指针是我们学习C语言的一大瓶颈,用分离链接散列算法的一大缺点也是因为他用到了指针,涉及到了内存分配。开放定址散列法是另一种解决冲突而不需要用到指针的算法。
开放地址散列法的基本思想是,遇到了冲突,我们就用另一套法则,将此关键字放在其他的空缺位置上。显而易见,他的缺点是散列表的创建必须足够大,才能够容许我们进行相关的操作,比起分离链接法来讲,虽然算法速度上有所提升,但是内存浪费比较大。因为他的装填因子λ应该低于0.5
装填因子:散列表的装填因子(load factor)λ为散列表中的元素个数与散列表大小的比值。
1.线性探测法
在原来的散列表基础之上添加增量序列,(1,2……,TableSize-1)循环试探下一个存储地址。
只要表足够大,我们就能根据这个方法找到相应的位置,但是这样花费的时间更多,且浪费了很多空间,这样占据的单元也会开始形成一些区块,这样的结果称为一次聚集
2.平方探测 – 二次探测
平方探测法:以增量序列1²,-1²,2²,-2²,……,q²,-q² 且 q≤|TableSize/2|玄幻试探下一个存储地址
伪代码描述
类型声明
struct HashEntry
{
ElementType Element;
enum KindOfEntry Info;
};
typedef struct HashEntry Cell;
struct HashTbl
{
int TableSize;
Cell *TheCells;
}
初始化开放定址散列表
HashTable
InitializeTable(int TableSize)
{
HashTable H;
int i;
if(TableSize < MinTableSize)
{
Error("Table size too small");
return NULL;
}
/*Allocate table*/
H = malloc( sizeof(struct HashTbl));
if(H == NULL)
FatalError("Out of space!!");
H->TableSize = NextPrime(TableSize);
/*Allocate array of cells*/
H->TheCells = malloc( sizeof(cell) * H->TableSize);
if(H->TheCells == NULL)
FatalError(" Out of space !!");
for(i = 0; i < H->TableSize; i++)
H->TheCells[i].Info = Empty;
return H;
}
看得出来开放定址的初始化和分离链接的初始化大致相同。
Find例程
Position
Find(ElementType key, HashTable H)
{
Position CurrentPos;
int CollisionNum;
CollisionNum = 0;
CurrentPos = Hash(key, H->TableSize);
while(H->TheCells[ CurrentPos].Info !=Empty &&
H->TheCells[ CurrentPos].Element != key)
/*Probably need strcmp*/
{
CurrentPos += 2 * ++CollisionNum -1;
if(CurrentPos >= H->TableSize)
CurrentPos -= H->TableSize;
}
return CurrentPos; //return the position
虽然说平方探测派出了一次聚集,但是散列到同一位置上的那些元素将探测相同的备选单元。形成了二次聚集。