在这篇博客中介绍了基于线性探测解决哈希冲突的哈希表的基本操作。在这种方法中,当哈希表的负载因子较大时,在哈希表中查找数据时的搜索效率就比较低。本文中将介绍根据哈希桶解决哈希冲突的哈希表的一些基本操作。
这种解决哈希冲突的方法又称为开散列。
开散列
又称为链地址法。首先根据哈希函数计算出Key所在的下标位置,将下标相同但Key值不同的元素放在一条链表中,将该链表的头结点或头指针作为哈希表中的元素。这样对于哈希表中的每个元素,都可以视为一条链表。这样一个链表可以视为一个桶,所以这种解决哈希冲突的方法又称为哈希桶。
例如,现在要在哈希表中存放一组数据:1,6,5,12,11,15,25,7。哈希函数为hash(key) = key % m(m取10)。
首先根据哈希函数计算各key值所在的下标位置:
hash(1) = 1 hash(6) = 6 hash(5) = 5 hash(12) = 2
hash(11) = 1 hash(15) = 5 hash(25) = 5 hash(7) = 7
所以在哈希表中的存放为:
所以基于上述的哈希桶来构建一个哈希表,并进行一些基本操作。
1. 哈希表的结构定义
由上述定义我们知道,该哈希表由数组构成,数组中的元素为哈希表元素的指针。哈希表元素由一个键值对和指向下一个哈希表元素的指针构成。键值对与这篇博客中的类型相同。同时还需要定义哈希表中的实际元素个数(这里的哈希表元素个数与数组元素个数不是同一概念)。最后还需要指定哈希表的下标是由哪个哈希函数计算而得。
#define HASHMAXSIZE 1000//定义数组的最大长度
typedef int KeyType;
typedef int ValType;
//定义哈希表中的元素类型
//哈希表中的数组元素为节点指针类型,一个节点包含一个key,一个value,一个指向下一个节点的指针
typedef struct HashElemNode
{
KeyType key;
ValType value;
struct HashElemNode* next;
}HashElemNode;
typedef size_t (*HashFunc)(KeyType key);
//定义哈希表
typedef struct HashTable
{
HashElemNode* data[HASHMAXSIZE];//哈希表中的数组元素定义
size_t size;//哈希表的实际元素个数
HashFunc func;//哈希函数
}HashTable;
2. 哈希表的初始化
初始时,哈希表中并没有元素。所以,哈希表的成员size应置为0。
数组元素代表的是由相同下标的哈希表元素构成的单链表。因为单链表有带头结点和不带头节点之分。所以数组元素可以是链表的头结点,也可以是链表的头指针。如果是带头结点的单链表,此时需要将头结点的指针域置为NULL,数据域任