散列(哈希)表的简述:
散列是数据结构字典的另一种表示方法,它用一个散列函数把关键字映射到散列表的具体位置。
散列表是一种面向查找的数据结构,它回避了关键词之间不断比较的麻烦,直接一步到位找到关键词的位置,是高效的。
散列函数:
散列函数可以有很多种,常见的有
(1)平方区中法:把关键词平方后,取中间三位数字,适用于位数较小的关键词。
(2)求模取余法:用一个求模函数,取得余数,被除数一般小于等于元素数量的大小。
(3)分析抽取法:把一个关键词的部分数字抽取出来作为关键词,适用于有一些规律且较大的关键词。
这其中最常用的就是求模取余法,它能比较均匀的把关键词插入散列表中。
//定义散列函数
int Hash(int key)
{
return key%m; //函数为f(k) = k%m; 这里m取元素数量12
}
散列表冲突时的处理
当关键词k1和关键词k2,同时进行散列函数取值后,f(k1)=f(k2),这时,散列表就发生了冲突,冲突并不可怕,但冲突造成的溢出可怕,此时就要进行冲突的处理,常见的处理方法有下面几种:
(1)线性探查法:既然当前位置被占了,就继续找后面存着的空位,进行线性的查找,不浪费剩余的空间。
(2)链地址法:让冲突发生,直接在一个位置存储多个元素,此时就可以用到单链表,在一个节点储存一个单链表,单链表来存储多个元素,当然这样也会在遍历单链表时造成更多的性能损耗。
(3)公共溢出区法:再创建一个备用的散列表,如果溢出就按顺序存储到备用表,查找时如果没在主表找到,则在备用表进行线性查找,适用于溢出较少的情况。
散列表的初始化:
#define SUCCESS 1
#define UNSUCCESS 0
#define HASHSIZE 12 //散列表长为数组长
#define NULLKEY -32768 //空值
typedef int Status;
typedef struct HashTable
{
int *elem; //元素存在在动态数组
int count; //当前元素个数
};
int m = 0; //散列表表长 全局变量
//初始化散列表
Status InitHashTable(HashTable *h)
{
int i;
m = HASHSIZE; //散列表表长设为12
h->count = m; //元素个数
h->elem = new int[m]; //创建一个大小为HASHSIZE的int类型数组
for (int i = 0; i < m; i++)
{
h->elem[i] = NULLKEY; //将数组内元素设为空值
}
return SUCCESS;
}
散列表的插入:
这里使用的冲突处理方法为线性探查法:
//插入元素到散列表 按散列地址
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;
}