构造一个高效的哈希表:从基本思路到最终实现

哈希表是计算机科学中常用的数据结构之一,它提供了快速的查找、插入和删除操作。在本篇博客中,我们将探讨如何构造一个高效的哈希表,从最基本的思路逐步完善,直至最终实现。

1. 初始思路:使用布尔数组存储

我们最初的想法是使用一个布尔数组来存储哈希表的元素。具体来说,数组中的每个元素代表一个可能的键值,如果某个键存在于哈希表中,则将对应位置的布尔值设为true,否则设为false。这种方法的代码如下:

public class SimpleHashSet {private boolean[] present;public SimpleHashSet() {present = new boolean[2000000000];}public void add(int key) {present[key] = true;}public boolean contains(int key) {return present[key];}
}

这种方法的问题在于,它会浪费大量的内存空间,尤其是当哈希表中只有少量元素时。此外,它只能处理整数类型的键,无法处理其他类型的数据。

2. 使用哈希函数解决键的类型问题

为了解决只能处理整数类型键的问题,我们可以引入哈希函数将其他类型的键转换为整数。下面是一个将字符串转换为整数的示例:

public class DataIndexedEnglishWordSet {private boolean[] present;public DataIndexedEnglishWordSet() {present = new boolean[2000000000];}public void add(String key) {present[englishToInt(key)] = true;}public boolean contains(String key) {return present[englishToInt(key)];}private static int letterNum(String s, int i) {int ithChar = s.charAt(i);if (ithChar < 'a' || ithChar > 'z') {throw new IllegalArgumentException();}return ithChar - 'a' + 1;}private static int englishToInt(String s) {int intRep = 0;for (int i = 0; i < s.length(); i++) {intRep = intRep * 26;intRep += letterNum(s, i);}return intRep;}
}

虽然现在我们可以处理字符串类型的键,但仍然存在两个主要问题:内存浪费和处理哈希冲突的困难。

3. 引入开放地址法解决冲突

为了解决哈希冲突的问题,我们引入了开放地址法中的线性探查。当插入的位置已被占用时,通过顺序检查数组的下一个位置来解决冲突。

public class DataIndexedEnglishWordSet {private boolean[] present;public DataIndexedEnglishWordSet() {present = new boolean[2000000000];}public void add(String key) {int index = englishToInt(key);while (present[index] != false) {index = (index + 1) % present.length;}present[index] = true;}public boolean contains(String key) {int index = englishToInt(key);while (present[index] != false) {if (present[index]) {return true;}index = (index + 1) % present.length;}return false;}private static int letterNum(String s, int i) {int ithChar = s.charAt(i);if (ithChar < 'a' || ithChar > 'z') {throw new IllegalArgumentException();}return ithChar - 'a' + 1;}private static int englishToInt(String s) {int intRep = 0;for (int i = 0; i < s.length(); i++) {intRep = intRep * 26;intRep += letterNum(s, i);}return intRep;}
}

通过引入线性探查,我们可以更有效地处理哈希冲突,提高了哈希表的性能。

4. 使用取模操作减少空间浪费

为了减少空间浪费,我们修改了哈希函数,使用取模操作将键映射到数组索引上。

public class DataIndexedEnglishWordSet {private boolean[] present;public DataIndexedEnglishWordSet() {present = new boolean[101]; // 使用最小素数作为初始容量}public void add(String key) {int index = englishToInt(key);while (present[index] != false) {index = (index + 1) % present.length;}present[index] = true;}public boolean contains(String key) {int index = englishToInt(key);while (present[index] != false) {if (present[index]) {return true;}index = (index + 1) % present.length;}return false;}private static int letterNum(String s, int i) {int ithChar = s.charAt(i);if (ithChar < 'a' || ithChar > 'z') {throw new IllegalArgumentException();}return ithChar - 'a' + 1;}private int englishToInt(String s) {int intRep = 0;for (int i = 0; i < s.length(); i++) {intRep = intRep * 26;intRep += letterNum(s, i);}return intRep % present.length; // 修改为取模操作}
}

通过取模操作,我们将键均匀地映射到数组索引上,减少了空间浪费。

5. 动态更改哈希表大小以优化性能

为了进一步优化性能,我们添加了动态调整哈希表大小的功能。当负载因子超过阈值时,我们将重构哈希表并扩大其容量。

public class ImprovedHashSet {private boolean[] present;private int size;private static final float LOAD_FACTOR_THRESHOLD = 0.75f;public ImprovedHashSet() {present = new boolean[101];size = 0;}public void add(String key) {if (loadFactor() >= LOAD_FACTOR_THRESHOLD) {resize();}int index = englishToInt(key);while (present[index] != false) {index = (index + 1) % present.length;}present[index] = true;size++;}public boolean contains(String key) {int index = englishToInt(key);while (present[index] != false) {if (present[index]) {return true;}index = (index + 1) % present.length;}return false;}private float loadFactor() {return (float) size / present.length;}private void resize() {boolean[] oldPresent = present;int newCapacity = nextPrime(oldPresent.length * 2);present = new boolean[newCapacity];size = 0;for (boolean value : oldPresent) {if (value) {add(someKey); // 重新插入元素,假设someKey为原哈希表的所有键}}}private int nextPrime(int n) {while (true) {if (isPrime(n)) {return n;}n++;}}private boolean isPrime(int n) {if (n <= 1) {return false;}if (n <= 3) {return true;}if (n % 2 == 0 || n % 3 == 0) {return false;}int i = 5;while (i * i <= n) {if (n % i == 0 || n % (i + 2) == 0) {return false;}i += 6;}return true;}private static int letterNum(String s, int i) {int ithChar = s.charAt(i);if (ithChar < 'a' || ithChar > 'z') {throw new IllegalArgumentException();}return ithChar - 'a' + 1;}private int englishToInt(String s) {int intRep = 0;for (int i = 0; i < s.length(); i++) {intRep = intRep * 26;intRep += letterNum(s, i);}return intRep % present.length;}
}

通过动态调整哈希表大小,我们可以根据负载因子的变化来合理分配内存,提高了哈希表的效率和性能。

总结

在本篇博客中,我们从最基本的思路出发,逐步完善了一个高效的哈希表的实现。我们通过引入哈希函数解决键的类型问题,使用开放地址法解决冲突,通过取模操作减少空间浪费,并最终实现了动态更改哈希表大小以

优化性能。这些优化措施使得我们的哈希表在处理不同类型的键和不同规模的数据时都能保持高效运行。

希望通过本文的介绍,你对构造高效的哈希表有了更深入的理解,也能在实际应用中更好地利用哈希表这一重要的数据结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/21961.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AIGC 全面介绍

随着人工智能技术的不断进步&#xff0c;生成式人工智能&#xff08;AI Generated Content, AIGC&#xff09;成为了一个日益热门的话题。AIGC 指利用人工智能技术生成各类内容&#xff0c;包括文本、图像、音频、视频等。与传统的内容生成方法相比&#xff0c;AIGC 具有速度快…

谷歌创新框架:从非结构化数据,实现多模态学习

看、听、说的多模态已成为主流大模型的重要功能之一。但在数据爆炸时代&#xff0c;大模型学习文本类的结构化数据相对还好一些&#xff0c;但要去学习视频、音频、图片等非结构化数据非常困难。 目前&#xff0c;从结构化和非结构化数据实现多模态学习&#xff0c;会随着模态…

RK3588 VOP图层分配介绍

RK3588 VOP图层分配介绍 RK3588图层介绍 RK3588有8个图层&#xff0c;分别是Custer 0/1/2/3 和Esmart 0/1/2/3&#xff0c;两种图层的能力不一样&#xff0c;具体如下&#xff1a; Custer 分辨率&#xff1a;最大分辨率包括两种合并集群和单集群&#xff0c;分别为7680x432…

QT_UI设计

mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE //命名空间 namespace Ui { class MainWindow; } //ui_MainWindow文件里定义的类&#xff0c;外部声明 QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_O…

AccessibilityEvent的生成和处理

在 Android 框架层&#xff0c;AccessibilityEvent 的生成和处理是通过系统的 UI 框架和辅助功能服务框架密切协作来实现的。这个机制涉及几个关键的部分&#xff1a;UI 组件、辅助功能服务、事件监听和事件分发。以下是对这些部分和它们如何协同工作的详细解释&#xff1a; 1…

httprunner接口自动化测试框架使用说明【保姆级教程】

背景介绍&#xff1a; httprunner是国内开源的一个接口自动化框架&#xff0c;已经有部分公司开始使用这种框架来完成自己公司的接口自动化编写&#xff0c;本文主要是从简单的流程上去讲解咋使用的&#xff08;PS&#xff1a;开发者本尊的官网教程写的是真的烂。。。&#xf…

JVM调优实战

如果老年代能回收掉大部分&#xff0c;说明年轻代太小了&#xff0c;放不下 OOM 1数据量一次性申请的内存过多&#xff0c;比如数据库查询返回值大多&#xff0c;所以做个分页 2.并发过高的情况下&#xff0c;一些连接未释放 3.堆内存不够

DP-Kmaens密度峰值聚类算法

我有个问题 关于 [密度值>密度阈值] 的判定这里&#xff0c;新进来的新数据怎么确定他的密度值&#xff1f;密度阈值又是怎样确定的呢&#xff1f;

正则表达式 0.1v

正则表达式 扩展 --> :% s/\///g //文件里面所有的 / 去掉 * 通配符 \ //转义&#xff0c;让字符变成原本的意思 ^ //行首 $ //行尾 [0-9] //数字 [a-z] //小写字母 [A-Z] //大写字母 把文件的小写字母替换为大写字母&#xff1f; 固定写法 :% s/[a-…

Vscode git 插件

超好用的git记录 软件 安装之后&#xff0c;鼠标在哪一行就可以看最新一次是谁提交的&#xff0c;真的超好用&#xff01;&#xff01;&#xff01;

43页 | 2024年企业级BI平台白皮书(免费下载)

【1】关注本公众号&#xff0c;转发当前文章到微信朋友圈 【2】私信发送 2024年企业级BI平台白皮书 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。 诚挚邀请您微信扫码加入以下方案驿站知识星球&#xff0c;获取上万份PPT/WORD解决方案&#xff01;&#xff01;&…

【NOI】C++程序结构入门之循环结构二-for循环

文章目录 前言一、for循环1.导入2.语法3.使用场景4.条件控制5.小结 二、例题讲解问题&#xff1a;1264 - 4位反序数问题&#xff1a;1085 - 寻找雷劈数问题&#xff1a;1057 - 能被5整除且至少有一位数字是5的所有整数的个数问题&#xff1a;1392 - 回文偶数&#xff1f;问题&a…

Linux命令 netstat -anp | grep 的用法

文章目录 1、第一种解释2、第二种解释3、第三种解释4、第四种解释5、第五种解释6、netstat --help 在Windows中&#xff0c;杀死端口占用的博客链接 1、第一种解释 在Unix和Linux系统中&#xff0c;netstat -anp 命令用于显示所有的网络连接&#xff08; -a 表示所有&#xff…

文件md5加密

使用场景&#xff1a;为了避免上传资源空间的浪费&#xff0c;通过对文件进行md5摘要加密获取唯一的值&#xff0c;从数据库中查询是否已有该md5码存在&#xff0c;不存在的就上传&#xff0c;存在的话使用之前已存储的文件信息。 如何加密 下载插件browser-md5-file 【之前有…

maridb10.4.30数据库数据迁移

1.新建数据存储文件夹&#xff0c;例如E:\maridb_data 2.修改原数据所在目录的my.ini文件&#xff0c;例如D:\Program Files\MariaDB 10.4\data\my.ini 3.剪切除my.ini文件外的其他所有文件到迁移目的地文件(E:\maridb_data) 结果如下&#xff1a; 原数据文件目录&#xff1a…

聊聊限流的一些事儿

一、背景 最近几年&#xff0c;随着微服务的流行&#xff0c;服务与服务之间依赖越来越强&#xff0c;调用也越来越复杂&#xff0c;服务间的稳定性变突显出来。特别是在遇到突发请求时&#xff0c;常常需要通过缓存、限流、熔断降级、负载均衡等多种方式保证服务的稳定性。其…

C++命名空间(详解)

C基础语法 C基于C语言的改进&#xff1a;c在C语言的基础上引入并扩充了面向对象的概念 C基础概念&#xff1a;C是基于C语言而产生的,它即可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计 在1998年 出现C98…

爱普生差分晶振在光模块中的重要角色

光模块是现代通信设备中的重要组成部分&#xff0c;主要用于实现光电转换和信号传输&#xff0c;它是一种将光信号转换为电信号&#xff0c;或者将电信号转换为光信号的设备。在光纤通信中&#xff0c;光模块扮演着至关重要的角色。 光模块的主要组成部分包括光源、光接收器、…

OSPF学习笔记(状态机)

1、邻居关系 OSPF设备启动后&#xff0c;会通过OSPF接口向外发送Hello报文&#xff0c;收到Hello报文的OSPF设备会检查报文中所定义的参数&#xff0c;如果双方一致就会形成邻居关系&#xff0c;两端设备互为邻居 2、邻接关系 形成邻居关系后&#xff0c;如果两端设备成功交…

【代码随想录】【算法训练营】【第27天】 [39]组合总和 [40] 组合总和II [131]分割回文串

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day26&#xff0c; 休息的周末~ day 27&#xff0c;周一&#xff0c;库存没了&#xff0c;哭死~ 题目详情 [39] 组合总和 题目描述 39 组合总和 解题思路 前提&#xff1a;组合的子集问题&…