散列表查找
散列表查找是一种特殊的查找方法,它能够通过对关键字的值快速定位待查找元素的位置。再查找方面,散列表有着极高的效率。但是一个散列表或多或少会存在冲突,为了解决冲突,我们设计了两个方法,一个是开放定址法,另一个就是拉链法。
开放定址法又分为线性探查法,二分探查法和双重散列法,就效率而言,双重散列法的效率是最优的,但是其中最为直观的方法还是线性探查法。
基本思想
使用某种方法在散列表中形成一个探查序列,沿着此序列逐个单元进行比较,直到找到一个空的单元并将新节点插入其中。假设待散列空间为T[0…m-1],散列函数为H(KEY),则开放定址法的一般形式为:
Hi = (H(KEY)+Di)%m 且 i>=0&&i<=m-1
而拉链法就是开放定址法在链式存储上的一章算法体现而已。理解了开放定址法,拉链法就不会让人感到晦涩难懂。
线性探查法解决冲突的查找和插入算法
#define M 1003 //M为表长
typedef struct { //定义一个结构体NodeType
KeyType key;
DataType data;
}NodeType;
typedef NodeType HashTable[M]; //定义一个哈希表,表长为M
//定义散列函数
int h(KeyType HT,KeyType K,int m){
return K%m; //除余法定义散列函数
}
//采用线性探查法的散列表查找算法
int HashSearch1(HashTable HT,KeyType K,int m){
int d,temp;
d = h(K,m); //调用散列函数获取散列地址
temp = d; //temp防止进入重复循环
while(HT[d].key!=-32768) //如果Key不为空
{
if(HT[d].Key == K){ //查找成功
return d;
}else{
d = (d+1)%m; //计算下一个地址
}
if(d==temp){ //循环一次
return -1; //找不到可用地址返回-1
}
return d;
}
}
//在散列表上插入一个节点的算法
int HashInsert1(HashTable HT,NodeType s,int m){
int d;
d = HashSearch1(HT,s,m); //查找可用地址
if (d==1)return -1;
else{
if (HT[d].key == s.key){return 0;} //表中已有该节点
else{
HT[d] =s ; //插入结点
return 1; //返回1表示成功
}
}
}
拉链法建立散列表上的查找和插入运算
//类型定义
typedef struct node{
KeyType key;
DataType data;
struct HTNode* HT[M];
}
int h(KeyType HT,KeyType K,int m){
return K%m; //除余法定义散列函数
}
//查找算法
HTNode* HashSeaerch2(HT T,KeyType K,int m){
HTNode* p = T[h(K,m)]; //取K所在链表的头指针
while(p!=NULL && p->key!=K){ //如果当前链表头指针不为空,且不为待查找结点
p=p->next; //遍历
}
return p;
}
//插入算法
int HashInsert2(HT T,HTNode* s,int m)
{
int d;
HTNode* p = HashSearch2(T,s->key,m); //查找当前结点
if(p!=NULL)return 0; //表明表中没有待插入结点
else{ //将*s插入在相应链表的表头上
d = h(s->key,m); //获取待插入结点的头指针
s->next=T[d];
T[d]=s;
return 1; //插入成功
}
}