使用分离链接法实现散列表时需要额外的空间来存储指针,而且需要给新单元动态分配空间,导致算法的速度减慢。开放定址法一次分配表的大小,可以使用线性散列,平方散列,双重散列等等方法,这些方法除了散列函数不相同之外,对于散列表的大小要求也不一样。平方散列需要使表的大小是存储元素的两倍以上,这样总能找到空槽来存放产生冲突的元素。(算法导论上有详细证明。)
下面程序使用的是平方探测散列:
/*
* hash.h
*
* Created on: Dec 12, 2016
* Author: csf
*/
#ifndef HASH_H_
#define HASH_H_
#define MAXTABLESIZE 200
#define MINTABLESIZE 1
typedef unsigned int Index;
typedef Index Position;
typedef int ElementType;
struct HashTbl;
typedef struct HashTbl *HashTable;
int IsPrime(int x);
int NextPrime(int y);
int Hash(ElementType key,int TableSize);
HashTable InitializeTable(int TableSize);
void DestroyTable(HashTable H);
Position Find(ElementType key,HashTable H);
void Insert(ElementType key,HashTable H);
ElementType Retrieve(Position P,HashTable H);
int Delete(ElementType key,HashTable H);
HashTable Rehash(HashTable H);
#endif /* HASH_H_ */
enum KindOfEntry {Legitimate,Empty,Deleted};
struct HashEntry
{
ElementType Element;
enum KindOfEntry Info;
};
typedef struct HashEntry Cell;
struct HashTbl
{
int TableSize;
Cell *TheCells;
};
源文件:
/*
* hash.c
*
* Created on: Dec 12, 2016
* Author: csf
*/
#include "hash.h"
#include "stdio.h"
#include "stdlib.h"
int IsPrime(int x) //判断素数
{
int i;
for(i=2;i*i<x;i++){
if(x%i==0)
return 0;
}
return 1;
}
int NextPrime(int y) //查找最邻近素数
{
while(1){
if(IsPrime(y))
return y;
else
y++;
}
}
int Hash(ElementType key,int TableSize) //简单取余数的散列函数
{
return key%TableSize;
}
HashTable InitializeTable(int TableSize) //初始化散列表
{
HashTable H;
int i;
if(TableSize<MINTABLESIZE)
return NULL;
H=(struct HashTbl*)malloc(sizeof(struct HashTbl));
H->TableSize=NextPrime(TableSize);
if(H==NULL)
return NULL;
H->TheCells=malloc(sizeof(Cell)*H->TableSize);
if(H->TheCells==NULL)
return NULL;
for(i=0;i<H->TableSize;i++)
{
H->TheCells[i].Info=Empty;
}
return H;
}
Position Find(ElementType key,HashTable H) //查找表内元素,没有该元素返回空槽
{
Position Pos;
int i;
i=0;
Pos=Hash(key,H->TableSize);
while(H->TheCells[Pos].Info!=Empty && H->TheCells[Pos].Element!=key)//字符串比较用strcmp
{
i+=1;
Pos=Pos+2*i-1;
if(Pos>=H->TableSize)
Pos-=H->TableSize;
}
return Pos;
}
void Insert(ElementType key,HashTable H) //插入新元素,元素存在则什么也不做
{
Position Pos;
Pos=Find(key,H);
if(H->TheCells[Pos].Info!=Legitimate)
{
H->TheCells[Pos].Info=Legitimate;
H->TheCells[Pos].Element=key;
}
}
void DestroyTable(HashTable H) //销毁散列表
{
free(H->TheCells);
free(H);
}
ElementType Retrieve(Position P,HashTable H) //根据元素位置得到元素值
{
return H->TheCells[P].Element;
}
int Delete(ElementType key,HashTable H) //删除元素,实际只是把元素标记为deleted
{
Position TempPos,Pos;
int i;
i=0;
Pos=Hash(key,H->TableSize);
TempPos=Pos;
if(H->TheCells[Pos].Element!=key)
{
i+=1;
Pos+=2*i-1;
if(Pos>H->TableSize)
Pos-=H->TableSize;
if(TempPos==Pos)
return 0;//没有找到该元素,退出避免死循环
}
H->TheCells[Pos].Info=Deleted;
return 1;
}
HashTable Rehash(HashTable H) //再散列,当填充因子大于0.5或者发生冲突无法找到空槽时使用
{
int i,OldSize;
Cell *OldCells;
OldSize=H->TableSize;
OldCells=H->TheCells;
H=InitializeTable(2*OldSize);
for(i=0;i<OldSize;i++)
{
if(OldCells[i].Info==Legitimate)
Insert(OldCells[i].Element,H);
}
free(OldCells);
return H;
}
void main() //测试用例
{
Position p;
HashTable table=InitializeTable(30);
Insert(0,table);
Insert(1,table);
Insert(81,table);
Insert(4,table);
Insert(64,table);
Insert(25,table);
Insert(16,table);
Insert(36,table);
Insert(9,table);
Insert(49,table);
p=Find(81,table);
printf("%d \n",table->TheCells[p].Element);
if(Delete(81,table))
{
printf("Deleted\n");
}
if(Delete(100,table))
{
printf("NoData\n");
}
DestroyTable(table);
}