数据结构与算法分析实验13 实现哈希表

实现哈希表

  • 1.哈希表介绍
    • 哈希函数
    • 冲突处理
    • 哈希表的操作
    • 哈希表的应用
  • 2.上机要求
  • 3.上机环境
  • 4.程序清单(写明运行结果及结果分析)
    • 4.1 程序清单
      • 4.1.1 头文件Hash.h 内容如下:
    • 4.1.2 实现文件Hash.cpp 内容如下:
      • 4.1.3 源文件main.cpp 内容如下:
    • 4.2 实现展效果示
  • 5.上机体会

1.哈希表介绍

哈希表(Hash Table)是一种基于哈希函数实现的数据结构,用于存储键值对。它通过将键映射到表中的位置来实现快速的数据访问。哈希表的核心思想是利用哈希函数将键转换为索引,从而在常数时间内进行插入、删除和查找操作。

哈希函数

哈希函数是哈希表的核心组件,它将任意大小的数据映射到固定大小的值,通常是一个整数。理想的哈希函数应具备以下特性:

  • 一致性:相同的输入应始终产生相同的输出。
    均匀分布:哈希值应尽可能均匀地分布在哈希表的索引范围内,以减少冲突。
  • 高效性:哈希函数的计算应尽可能快速。

冲突处理

由于哈希函数的输出范围有限,不同的键可能会映射到相同的索引,这种情况称为冲突。常见的冲突处理方法包括:

  • 链地址法:每个哈希表的索引位置存储一个链表,所有映射到该索引的键值对都存储在这个链表中。
  • 开放地址法:当发生冲突时,通过某种探测方法(如线性探测、二次探测)在哈希表中寻找下一个可用的位置。

哈希表的操作

哈希表支持以下基本操作:

  • 插入:通过哈希函数计算键的索引,将键值对存储在相应的位置。如果发生冲突,使用冲突处理方法解决。
  • 查找:通过哈希函数计算键的索引,查找对应的值。如果发生冲突,使用冲突处理方法继续查找。
  • 删除:通过哈希函数计算键的索引,删除对应的键值对。如果发生冲突,使用冲突处理方法找到并删除。

哈希表的应用

哈希表广泛应用于各种场景,包括:

  • 数据库索引:用于快速查找数据库中的记录。
  • 缓存系统:用于存储和快速访问缓存数据。
  • 字典和集合:用于实现高效的字典和集合数据结构。

2.上机要求

实现Hash表,并基于Hash表实现HashMap

3.上机环境

visual studio 2022
Windows11 家庭版 64位操作系统

4.程序清单(写明运行结果及结果分析)

4.1 程序清单

4.1.1 头文件Hash.h 内容如下:

#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);				//打印哈希表

4.1.2 实现文件Hash.cpp 内容如下:

#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;
		}
	}
}

4.1.3 源文件main.cpp 内容如下:

#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;
}

4.2 实现展效果示

编译运行源文件,首先输出创建的hash表,并提醒我们输入键值对,如下图所示:
数据结构与算法分析实验13 实现哈希表_第1张图片

输入本人学号,姓名作为键值对,成功更新hash表,提醒我们进行删除操作:
数据结构与算法分析实验13 实现哈希表_第2张图片删除bad mood 对应记录,提醒删除成功,提醒我们进行修改:
例如,将dog 对应的值改为“人类最好的朋友”,修改效果如下:
数据结构与算法分析实验13 实现哈希表_第3张图片查找dog的值,程序打印显示并正常退出。
数据结构与算法分析实验13 实现哈希表_第4张图片

5.上机体会

哈希表是一种用于快速插入、查找和删除元素的数据结构,它在理想的情况下可以提供 O(1) 的查找、插入和删除时间复杂度,是功能强大的数据结构。我们首先需要构建哈希函数:哈希函数的选择对于哈希表的性能至关重要。一个好的哈希函数应该尽量避免冲突,即尽量将不同的元素映射到不同的数字上。如果哈希函数设计不当,可能会导致哈希表中出现大量的冲突,从而降低查询和插入的效率。
负载因子:负载因子是哈希表中元素个数与桶数的比值。负载因子过高会导致哈希表中冲突的概率增加,从而降低性能。而过低的负载因子会导致空间浪费。因此,需要选择一个合适的负载因子,以在性能和空间效率之间取得平衡。
解决冲突的方法:当发生冲突时,需要有一种方法来解决冲突。常见的解决冲突的方法包括开放地址法和链地址法,开放地址法通过探测相邻的空位来寻找空闲位置,而链地址法将冲突的元素存储在一个链表中。选择合适的解决冲突的方法可以提高哈希表的性能,在这里,我们使用了链地址法。
哈希表的扩容:随着元素的不断插入,哈希表可能会出现负载因子过高的情况。为了保持哈希表的性能,需要在适当的时候对哈希表进行扩容。扩容操作通常需要重新计算元素的哈希值,并将其分布到新的桶中。在实现扩容操作时,需要考虑数据的迁移和复制。
性能分析:在实验过程中,可以对哈希表的性能进行分析。可以统计插入、查找和删除操作的时间复杂度,并与理论上的时间复杂度进行比较。如果实际性能与理论性能存在较大差距,可能需要对哈希表的实现进行优化。

你可能感兴趣的:(数据结构与算法分析实验,散列表,哈希算法,数据结构,c++)