简介

哈希表通过键值(key)来查找数据(value) 通常可以以极低的时间复杂度查找数据。

哈希表的底层通常是支持随机访问的容器。由于键值和数据值一一对应,哈希表支持以table[key]的形式访问数据。

为了减少空白位置造成的空间浪费,哈希表通过哈希函数将键映射到表中的一个索引位置。 哈希函数

当两个不同的键通过哈希函数映射到相同的数组索引位置时,就会发生哈希冲突。

解决哈希冲突的常用方法:

  1. 链式地址法
  2. 开放定址法

链式法在哈希表的每个位置存储一个链表,冲突的元素会被插入链表。

开放定址法会在数组中找一个空位存放冲突的元素。

本文实现的哈希表使用除留余数法映射键值,使用链式地址法处理哈希冲突。

哈希表类的使用

通过哈希表的构造函数创建一个哈希表对象

1
hashtable h1(12); // 创建一个大小为传入参数Size的哈希表

调用哈希表对象的成员函数Insert向哈希表内插入一个节点

1
h1.Insert(10,25);

如果键值已经存在,将替换原来的值。

GetValue()会返回哈希表中键值(key)对应的值(value) 如果键值不存在,则返回-1

GetKeyNode()会返回对应键值的节点(Hnode) 如果键值不存在,返回nullptr

哈希表类重载了[]操作符,可以通过h1[key]的形式访问数据

哈希表类的析构函数会在对象销毁时自动释放对象开辟的内存。

实现

哈希表类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class hashtable
{
public:
hashtable(int);
~hashtable();
Hnode* GetKeyNode(int);
int GetValue(int);
void Insert(int,int);
int operator[](int);
private:
Hnode**data;
int HashNum;
int Size;
int hash(int);
};

哈希表构造函数实现

1
2
3
4
5
hashtable::hashtable(int Size) {
data = new Hnode*[Size]();
HashNum = Size;
this->Size = Size;
}

创建一个哈希表对象时,构造函数会根据传入的参数Size开辟底层数组data,并初始化成员变量HashNum和Size

本实现使用除留余数法实现哈希函数

1
2
3
int hashtable::hash(int key) {
return key % HashNum;
}

GetKeyNode()函数在key对应的序列中查找并返回对应key值的节点。如果没有找到,返回nullptr

1
2
3
4
5
6
7
8
9
10
Hnode*hashtable::GetKeyNode(int key) {
Hnode * target = data[hash(key)];
if(target == nullptr) {
return nullptr;
}
while (target->next!=nullptr&&target->key!=key) {
target = target->next;
}
return target->key == key ? target : nullptr;
}

Insert()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void hashtable::Insert(int key,int val) {
Hnode * key_node;
if(key_node = GetKeyNode(key)) {
key_node->key = key;
key_node->val = val;
return;
}
key_node = data[hash(key)];
Hnode * newnode = BuyNewnode(key,val);
if(key_node==nullptr) {
data[hash(key)] = newnode;
return;
}
while (key_node->next) {
key_node = key_node->next;
}
key_node->next = newnode;
}