1.散列表查找算法实现
首先是需要定义一个散列表的结构和一些相关的函数。其中HashTable就是散列表结构,结构当中的elem为一个动态数组。
#define SUCCESS 1
#define UNSUCCESS 0
#define HASHSIZE 12 //定义散列表长为数组的长度
#define NULLKEY -32768
typedef struct
{
int *elem; //数据元素存储基址,动态分配数组
int count; //当前数据元素个数
}HashTable;
int m=0; //散列表长,全局变量
有了结构定义就可以对散列表进行初始化。
/* 初始化散列表 */
Status InitHashTable (HashTable *H)
{
int i;
m=HASHSIZE;
H->count = m;
H->elem= (int *)malloc(m*sizeof(int));
for(i=0;i<m;i++)
H->elem[i]=NULLKEY;
return OK;
}
为了插入时计算地址,我们需要定义散列函数,散列函数可以根据不同情况更改算法。
/* 散列函数 */
int Hash(int key)
{
return key % m; //除留余数法
}
初始化完成后,我们可以对散列表进行插入操作。假设我们插入的关键字集合为{12,67,56,16,25,37,22,29,15,47,48,34}。
/* 插入关键字进散列表 */
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; //开放定址法的线性探测
if (H.elem[*addr] == NULLKEY || *addr == Hash(key))
{ //如果循环回到原点
return UNSUCCESS; //则说明关键字不存在
}
}
return SUCCESS;
}
查找的代码如插入的非常类似,只需做一个不存在关键字的判断而已。
2.散列表查找性能分析
如果没有冲突,散列查找是所有查找中效率最高的,为O(1),可惜只是如果。
散列查找的平均查找长度影响因素有:
1.散列函数是否均匀,散列函数的好坏直接决定着出现冲突的频繁程度。
2.处理冲突的方法,处理方法不同,也会影响平均查找长度。比线性探测处理冲突可能会产生堆积,不如二次探测法;而链地址探测法不会产生任何堆积。
3.散列表的装填因子。所谓的装填因子α=填入表中的记录个数/散列表长度。α越大则产生冲突的可能性就越大。通常将散列表的空间设置得比查找集合大,虽然浪费了一定的空间,但是得到了较高的查找效率。