哈希表(Hash Table)是一种基于哈希函数实现的数据结构,用于存储键值对。它通过将键映射到表中的位置来实现快速的数据访问。哈希表的核心思想是利用哈希函数将键转换为索引,从而在常数时间内进行插入、删除和查找操作。
哈希函数是哈希表的核心组件,它将任意大小的数据映射到固定大小的值,通常是一个整数。理想的哈希函数应具备以下特性:
由于哈希函数的输出范围有限,不同的键可能会映射到相同的索引,这种情况称为冲突。常见的冲突处理方法包括:
哈希表支持以下基本操作:
哈希表广泛应用于各种场景,包括:
实现Hash表,并基于Hash表实现HashMap
visual studio 2022
Windows11 家庭版 64位操作系统
#pragma once
#include
#include
#define DIV 13
using namespace std;
typedef int status;
typedef struct HashNode {
string key;
string val;
struct HashNode* next;
}hNode,*phNode;
typedef struct HashMap {
hNode map[DIV];
}hMap;
status hash_fun(string str); //将字符转为数字的函数
status hash_ini(hMap& map); //初始化hashMap
status hash_add(hMap& map, hNode pair); //增加键值对
status hash_add(hMap& map); //增加键值对
status hash_del(hMap& map); //删除键值对
status hash_mdf(hMap& map); //修改
status hash_fnd(hMap& map); //查找
void hash_shw(hMap map); //打印哈希表
#include"Hash.h"
status hash_fun(string str){
int ret = 0;
int len = str.size()-1;
ret = (str[0] - 'a' + 1) * (str[len] - 'a' + 1) + len;
ret %= DIV;
return ret;
}
status hash_ini(hMap& map){
for (int i = 0; i < DIV; i++) {
map.map[i].key = "";
map.map[i].val = "";
map.map[i].next = NULL;
}
return 0;
}
status hash_add(hMap& map, hNode pair){
int index = hash_fun(pair.key); //得到下标
if (map.map[index].key == "") { //如果此位置没有数据
map.map[index].key = pair.key;
map.map[index].val = pair.val;
}
else { //如果此位置有数据
phNode move = &map.map[index]; //创建move
while (move->next != NULL) move = move->next;
//当move不指在链表最后一个节点上时一直往后走
move->next = new hNode; //malloc
move=move->next; //把move作为链表结尾
move->key = pair.key; //完成数据插入
move->val = pair.val;
move->next = NULL;
}
return 0;
}
status hash_del(hMap& map){
string key;
cout << "请输入要删除的键:"; cin >> key;
int index = hash_fun(key);
phNode move = &map.map[index];
phNode front = move; //move的前一个节点
while (move->key != key) { //找到对应节点
front = move;
move = move->next;
if (move == NULL) { cout << "没有对应key值" << endl; return 1; }
}
//如果找到了键值
if (move == &map.map[index]) { //如果是第一个元素,要注意如何"删除"
move->key = "";
move->val = "";
cout << "删除" << key << "成功" << endl;
}
else {
front->next = move->next; //move的下一个节点
delete move; //删除move
cout << "删除" << key << "成功" << endl;
}
return 0;
}
status hash_add(hMap& map)
{
string key; string val;
cout << "input key:"; cin >> key;
cout << "input value:"; cin >> val;
int index = hash_fun(key); //得到下标
if (map.map[index].key == "") { //如果此位置没有数据
map.map[index].key = key;
map.map[index].val = val;
}
else { //如果此位置有数据
phNode move = &map.map[index]; //创建move
while (move->next != NULL) move = move->next;
//当move不指在链表最后一个节点上时一直往后走
move->next = new hNode; //malloc
move = move->next; //把move作为链表结尾
move->key = key; //完成数据插入
move->val = val;
move->next = NULL;
}
return 0;
}
status hash_mdf(hMap& map){
string key;
cout << "请输入要修改的键:"; cin >> key;
int index = hash_fun(key);
phNode move = &map.map[index];
while (move->key != key) { //找到对应节点
move = move->next;
if (move == NULL) { cout << "没有对应key值" << endl; return 1; }
}
//如果找到了键值
move->val = "";
cout << "请输入新的值:"; cin >> move->val;
cout << "修改完成" << endl;;
return 0;
}
status hash_fnd(hMap& map){
string key;
cout << "请输入查找的键:"; cin >> key;
int index = hash_fun(key);
phNode move = &map.map[index];
while (move->key != key) { //找到对应节点
move = move->next;
if (move == NULL) { cout << "没有对应key值" << endl; return 1; }
}
//如果找到了键值
cout << move->key + "的值为" + move->val << endl;
return 0;
}
void hash_shw(hMap map){
cout << "hash table:" << endl;
for (int i = 0; i < DIV; i++) {
if (map.map[i].key != "" || map.map[i].next != NULL) { //如果对应位置有数据(请避免第一个键值对的影响)
cout << "hash[" << i << "]:";
phNode move = &map.map[i];
while (move != NULL) {
if (move->key != "") {
cout << " -> key:" + move->key + " value:" + move->val;
}
move = move->next;
}
cout << endl;
}
}
}
#include"Hash.h"
int main() {
hMap map;
hash_ini(map);
hash_add(map, { "hello","你好" });
hash_add(map, { "beautiful","漂亮" });
hash_add(map, { "interesting","有趣" });
hash_add(map, { "fire","火焰" });
hash_add(map, { "sleep","睡觉" });
hash_add(map, { "mysql","数据库" });
hash_add(map, { "any","一切" });
hash_add(map, { "sky","天空" });
hash_add(map, { "badmood","坏心情" });
hash_add(map, { "artist","艺术家" });
hash_add(map, { "mood","心情" });
hash_add(map, { "success","成功" });
hash_add(map, { "stupid","愚昧的" });
hash_add(map, { "copy","拷贝" });
hash_add(map, { "bubble","泡泡" });
hash_add(map, { "cat","猫猫" });
hash_add(map, { "dog","狗狗" });
hash_shw(map);
hash_add(map); //增
hash_shw(map);
hash_del(map); //删
hash_shw(map);
hash_mdf(map); //改
hash_shw(map);
hash_fnd(map); //查
return 0;
}
编译运行源文件,首先输出创建的hash表,并提醒我们输入键值对,如下图所示:
输入本人学号,姓名作为键值对,成功更新hash表,提醒我们进行删除操作:
删除bad mood 对应记录,提醒删除成功,提醒我们进行修改:
例如,将dog 对应的值改为“人类最好的朋友”,修改效果如下:
查找dog的值,程序打印显示并正常退出。
哈希表是一种用于快速插入、查找和删除元素的数据结构,它在理想的情况下可以提供 O(1) 的查找、插入和删除时间复杂度,是功能强大的数据结构。我们首先需要构建哈希函数:哈希函数的选择对于哈希表的性能至关重要。一个好的哈希函数应该尽量避免冲突,即尽量将不同的元素映射到不同的数字上。如果哈希函数设计不当,可能会导致哈希表中出现大量的冲突,从而降低查询和插入的效率。
负载因子:负载因子是哈希表中元素个数与桶数的比值。负载因子过高会导致哈希表中冲突的概率增加,从而降低性能。而过低的负载因子会导致空间浪费。因此,需要选择一个合适的负载因子,以在性能和空间效率之间取得平衡。
解决冲突的方法:当发生冲突时,需要有一种方法来解决冲突。常见的解决冲突的方法包括开放地址法和链地址法,开放地址法通过探测相邻的空位来寻找空闲位置,而链地址法将冲突的元素存储在一个链表中。选择合适的解决冲突的方法可以提高哈希表的性能,在这里,我们使用了链地址法。
哈希表的扩容:随着元素的不断插入,哈希表可能会出现负载因子过高的情况。为了保持哈希表的性能,需要在适当的时候对哈希表进行扩容。扩容操作通常需要重新计算元素的哈希值,并将其分布到新的桶中。在实现扩容操作时,需要考虑数据的迁移和复制。
性能分析:在实验过程中,可以对哈希表的性能进行分析。可以统计插入、查找和删除操作的时间复杂度,并与理论上的时间复杂度进行比较。如果实际性能与理论性能存在较大差距,可能需要对哈希表的实现进行优化。