Javascript数据结构——哈希表

news2024/10/28 20:49:35

18_哈希表_深入链地址法_哔哩哔哩_bilibili

Java [ 基础 ] HashMap详解 ✨_java hashmap-CSDN博客

哈希表(Hash Table),又称为散列表,是一种通过哈希函数组织数据以实现快速访问的数据结构。下面将从其概述、底层实现和前端应用场景等方面进行详细阐述。

概述

哈希表的基本思路是,设要存储的元素个数为n,设置一个长度为m(m≥n)的连续内存单元,以每个元素的关键字k为自变量,通过哈希函数把k映射为内存单元的地址(下标),并把该元素存储在这个内存单元中,如此构造的存储结构称为哈希表。简单来说,哈希表就像是一个巨大的书架,每个书架格都有一个编号(哈希值),而每个编号下都可以存放一本书(键值对)。当你想要找一本书时,只需知道它的编号,就能迅速定位到它所在的位置。

哈希函数是哈希表的核心,它接收一个输入(键),并返回一个输出(哈希值),这个哈希值通常是一个整数,用于确定键在表中的位置。理想情况下,哈希函数应该尽可能减少冲突(不同键产生相同哈希值的情况)。当两个键产生相同的哈希值时,就需要冲突解决机制。常见的冲突解决方法有开放寻址法和链地址法。

相关概念

一、哈希化

哈希化,又称散列,是通过关于键值(key)的函数,将数据映射到内存存储中的一个位置来访问的过程。这个过程称为哈希,而这个映射函数则称为散列函数或哈希函数。哈希化通过计算缩小范围,先分类再查找,从而加快查找速度

二、哈希函数

哈希函数是指将哈希表中元素的关键键值映射为元素存储位置的函数。表示为:Addr=H(key),其中Addr表示存储地址,H表示哈希函数,key表示关键字。

常见的哈希函数构造方法有:

  1. 直接定址法:取Key或者Key的某个线性函数值为散列地址。Hash(k)=k,或者Hash(k)=a×k+b,(a、b均为常数)。
  2. 数字分析法:需要知道Key的集合,并且Key的位数比地址位数多,选择Key数字分布均匀的位作为散列地址。
  3. 平方取中法:取Key平方值的中间几位作为Hash地址。因为在设置散列函数时不一定知道所有关键字,选取哪几位不确定。一个数的平方的中间几位和数本身的每一位都有关,这样可以使随机分布的Key得到的散列地址也是随机分布的。
  4. 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。当Key的位数较多且数字分布均匀时,适合采用这种方案。具体的叠加方法有两种:移位法和折叠法。
  5. 除留余数法:取关键字被某个除数p求余,得到的余数作为散列地址。即H(Key)=Key%p。这是最常用的构造哈希函数的方法。
  6. 随机数法:选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key)=random(key),其中random为随机函数。通常用于关键字长度不等时采用此法。

哈希函数的选择应尽量减少产生冲突,即不同的Key值对应同一个Hash地址的情况。但不管选用何种散列函数,都不可避免地会产生冲突。

三、哈希表

哈希表(Hash Table)又称散列表,是一种数据结构,用于实现关联数组(Associative Array),即可以通过键(Key)来查找对应的值(Value)哈希表使用哈希函数将键转换为数组下标,从而实现快速查找、插入和删除操作。

哈希表的主要优点是查询速度非常快,时间复杂度接近O(1)。但是,哈希表也有缺点,即在数据量较大时,可能会出现哈希冲突。解决哈希冲突的方法有多种,如开放寻址法(Open Addressing)、链地址法(Chaining,又称拉链法)等。

  • 开放寻址法:如果h(k)已经被占用,则按一定的增量序列探查新的位置,直到找到一个空闲的位置为止。根据探查函数p(i)的不同,开放定址法又分为线性探查法、二次探查法、随机探查法和双散列函数法等。

不推荐:开放寻址,探测长度随着填充因子(数据项/哈希表长)增大而程指数趋势增长

  • 链地址法:将具有同一散列地址的记录存储在一条线性链表中。当发生冲突时,只需将新元素插入到对应链表的末尾即可。这种方法虽然解决了冲突问题,但增加了编程复杂度,并且可能导致链表过长,影响查找效率。

常见的哈希表实现有Java中的HashMap、Python中的字典(dict)等。

底层实现

  1. 哈希函数:哈希函数的设计至关重要,它决定了哈希表的性能和效率。一个好的哈希函数应该具有快速计算、均匀分布等特点。在JavaScript中,Object类型的哈希表实际上是通过链地址法来解决冲突的,即每个数组索引指向一个链表,链表中存储所有哈希值相同的键值对。

  2. 冲突解决

    • 链地址法:每个哈希表的槽位存储一个链表,所有哈希值相同的键值对都存储在这个链表中。这种方法在处理冲突时比较灵活,但可能会增加额外的空间开销。
    • 开放寻址法:当发生冲突时,通过一定的探测策略(如线性探测、二次探测、再哈希等)找到一个空闲的槽位来存储键值对。这种方法不需要额外的存储空间,但可能会增加查找时间。
  3. 动态扩容:当哈希表中的数据量增加到一定程度时,会进行动态扩容,以避免哈希冲突和保持查找效率。扩容通常涉及重新计算所有键值对的哈希值,并将它们重新插入到新的哈希表中。

前端应用场景

  1. 缓存系统:在Web开发中,经常需要将频繁访问的数据存储在缓存中以提高访问速度。哈希表以其快速查找的特性,成为了缓存系统的理想选择。

  1. 去重与计数:在处理需要快速去重或计数的场景时,哈希表表现出色。例如,在统计一个字符串中每个字符出现的次数时,可以使用哈希表来记录每个字符及其对应的计数。
  2. 路由表:在Web服务器中,路由表负责将URL映射到相应的处理函数。哈希表能够快速根据URL定位到处理函数,提高路由效率。
  3. 用户输入处理:在处理用户输入时,可以使用哈希表来快速检查输入数据是否已存在或是否满足某些条件。

总之,哈希表作为数据结构中的瑰宝,在JavaScript开发中发挥着不可替代的作用。通过深入理解哈希表的原理和实现方式,可以更好地利用这一强大工具来优化代码性能和提高开发效率。

手动实现哈希表

实现哈希表(Hash Table)通常涉及两个主要部分:哈希函数(hash function)和存储桶(buckets)。在 JavaScript 中,我们可以使用数组和链表来实现一个简单的哈希表。数组将作为存储桶的集合,而链表将用于处理哈希冲突(即多个键映射到同一个索引的情况)。

以下是一个简单的实现:

  1. 定义链表节点
    链表节点将存储键值对以及指向下一个节点的指针。
class ListNode {  
    constructor(key, value) {  
        this.key = key;  
        this.value = value;  
        this.next = null;  
    }  
}
  1. 定义哈希表
    哈希表将包含一个数组(存储桶)和一个哈希函数。
class HashTable {  
    constructor(size = 10) {  
        this.size = size;  
        this.buckets = new Array(size).fill(null);  
    }  
  
    // 简单的哈希函数  
    hashFunction(key) {  
        let hash = 0;  
        for (let i = 0; i < key.length; i++) {  
            hash = (hash * 31 + key.charCodeAt(i)) % this.size;  
        }  
        return hash;  
    }  
  
    // 插入键值对  
    set(key, value) {  
        const index = this.hashFunction(key);  
        const bucket = this.buckets[index];  
  
        if (bucket === null) {  
            // 如果桶为空,创建一个新的链表节点  
            this.buckets[index] = new ListNode(key, value);  
        } else {  
            // 如果桶不为空,遍历链表查找键或插入新节点  
            let currentNode = bucket;  
            while (currentNode !== null) {  
                if (currentNode.key === key) {  
                    // 如果键已存在,更新值  
                    currentNode.value = value;  
                    return;  
                }  
                if (currentNode.next === null) {  
                    // 链表末尾,插入新节点  
                    currentNode.next = new ListNode(key, value);  
                    return;  
                }  
                currentNode = currentNode.next;  
            }  
        }  
    }  
  
    // 获取值  
    get(key) {  
        const index = this.hashFunction(key);  
        const bucket = this.buckets[index];  
  
        if (bucket === null) {  
            // 如果桶为空,返回 undefined  
            return undefined;  
        } else {  
            // 遍历链表查找键  
            let currentNode = bucket;  
            while (currentNode !== null) {  
                if (currentNode.key === key) {  
                    return currentNode.value;  
                }  
                currentNode = currentNode.next;  
            }  
        }  
        // 如果键不存在,返回 undefined  
        return undefined;  
    }  
  
    // 删除键值对  
    remove(key) {  
        const index = this.hashFunction(key);  
        let bucket = this.buckets[index];  
  
        if (bucket === null) {  
            // 如果桶为空,不执行任何操作  
            return;  
        } else if (bucket.key === key) {  
            // 如果要删除的节点是桶的第一个节点  
            this.buckets[index] = bucket.next;  
        } else {  
            // 遍历链表查找并删除节点  
            let currentNode = bucket;  
            while (currentNode.next !== null) {  
                if (currentNode.next.key === key) {  
                    currentNode.next = currentNode.next.next;  
                    return;  
                }  
                currentNode = currentNode.next;  
            }  
        }  
    }  
}
  1. 使用哈希表
    现在我们可以创建哈希表实例并插入、获取和删除键值对。
const hashTable = new HashTable();  
hashTable.set('name', 'Alice');  
hashTable.set('age', 30);  
hashTable.set('city', 'New York');  
  
console.log(hashTable.get('name')); // 输出: Alice  
console.log(hashTable.get('age'));  // 输出: 30  
console.log(hashTable.get('city')); // 输出: New York  
  
hashTable.remove('age');  
console.log(hashTable.get('age'));  // 输出: undefined

这个简单的哈希表实现展示了如何使用数组和链表来处理哈希冲突,并通过哈希函数将键映射到数组索引。注意,这只是一个基础实现,实际生产中的哈希表可能会使用更复杂的哈希函数、动态数组大小调整(如哈希表的扩展和收缩)以及其他优化技术。

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

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

相关文章

【题解】【分治】——黑白棋子的移动

【题解】【递归】——黑白棋子的移动 黑白棋子的移动题目描述输入格式输出格式输入输出样例输入 #1输出 #1 提示 1.题意解析2.AC代码 黑白棋子的移动 通往洛谷的传送门 题目描述 有 2 n 2n 2n 个棋子排成一行&#xff0c;开始为位置白子全部在左边&#xff0c;黑子全部在右…

2024 前端面试题!!! html css js相关

常见的块元素、行内元素以及行内块元素&#xff0c;三者有何不同&#xff1f;​​​​​​​ HTML、XML、XHTML它们之间有什么区别&#xff1f;​​​​​​​ DOCTYPE(⽂档类型) 的作⽤ Doctype是HTML5的文档声明&#xff0c;通过它可以告诉浏览器&#xff0c;使用哪一个HTM…

Vue学习笔记(八)

透传attribute "透传attribute"指的是传递给一个组件&#xff0c;却没有被改组件声明为props或emits的attribute或者v-on事件监听器。最常见的例子就是class、style和id。 当一个组件以单个元素为根作渲染时&#xff0c;透传的attribute会自动被添加到根元素上。 …

WSGI、uwsgi与uWSGI

WSGI、uwsgi与uWSGI WSGI (web server gateway interface 网络服务网关接口) 只能用于python&#xff0c;是一个接口标准协议&#xff0c;django、flask自带有uwsgi 是WSGI的通信协议uWSGI是实现了uwsgi协议和WSGI服务的web服务器 他们是为了将web服务器与web框架连接起来 N…

《MYSQL 实战45讲》深入浅出ORDER BY底层

慢查询日志这个路径下&#xff0c;但是打不开 内存临时表 现在有一个业务功能是从一个单词表里面随机选出3个单词出来 这个表有10000条数据。 select word from words order by rank() limit 3 使用执行计划查看结果&#xff1a; select_type是SIMPLE表示改查询不包含子查询…

USB Type-C 受电端取电快充协议芯片,支持PD+QC+FCP+SCP+AFC快充协议

前言 随着科技的飞速发展&#xff0c;电子设备对于快速充电的需求日益增加。为了满足这一需求&#xff0c;市场上涌现出了众多快充技术和产品。其中&#xff0c;XSP08Q诱骗取电芯片以其卓越的性能和广泛的应用场景&#xff0c;成为了快充领域的一颗璀璨明星。本文将对XSP08Q P…

边缘计算网关在储能领域的应用-天拓四方

随着全球能源结构的转型和智能电网的快速发展&#xff0c;储能技术已成为推动能源革命的关键技术之一。在储能系统中&#xff0c;边缘计算网关作为一种新型的网络设备&#xff0c;发挥着至关重要的作用。 一、边缘计算网关在储能领域的应用 1、实时监控与控制 储能系统是一个…

hf_transformers-Quantization

bitsandbytes BitSandbytes 是将模型量化为 8 位和 4 位的最简单选项。8 位量化将 FP16 中的异常值与 INT8 中的非异常值相乘&#xff0c;将非异常值转换回 FP16&#xff0c;然后将它们相加以返回 FP16 中的权重。这减少了离群值对模型性能的降级影响。4 位量化进一步压缩模型…

【小白学机器学习22】 多变量分析:相关性分析,多变量回归,最小二乘法 ols等

目录 1 概念名词&#xff1a; 1.1 双变量分析与单因素分析 1.2 变化 1.3 共变 和 相关分析 Correlation analysis 1.4 回归分析 Regression analysis 1.5 相干和相关 1.5.1 相干relevant 1.5.2 相关Correlation 2 双变量分析 3 相关分析 3.1 是否相关 3.2 相关的…

Ping32为何是理想的加密工具?十大核心功能保障您的数据安全

在当今数字化时代&#xff0c;数据安全已成为企业和个人面临的重大挑战。随着信息泄露事件的频繁发生&#xff0c;选择一款可靠的加密工具至关重要。Ping32 作为一款备受赞誉的加密软件&#xff0c;以其十大核心功能&#xff0c;成为保障数据安全的理想选择。 一、强大的加密算…

React第十一章(useReducer)

useReducer useReducer是React提供的一个高级Hook,没有它我们也可以正常开发&#xff0c;但是useReducer可以使我们的代码具有更好的可读性&#xff0c;可维护性。 useReducer 跟 useState 一样的都是帮我们管理组件的状态的&#xff0c;但是呢与useState不同的是 useReducer…

JsonCpp库学习记录

使用源码的方式 到JsonCpp的开源库仓库下载最新的发行版本 解压压缩包 使用Python生成源码文件 在本路径下cmd打开控制台&#xff0c;使用python编译&#xff08;前提是python环境已安装&#xff09; python amalgamate.py 生成dist文件夹 jsoncpp为整合在一起的源码&#…

vue 解决:npm ERR! code ERESOLVE 及 npm ERR! ERESOLVE could not resolve 的方案

1、问题描述&#xff1a; 其一、需求为&#xff1a; 想要安装项目所需依赖&#xff0c;成功运行 vue 项目&#xff0c;想要在浏览器中能成功访问项目地址 其二、问题描述为&#xff1a; 在 package.json 文件打开终端平台&#xff0c;通过执行 npm install 命令&#xff0c…

6,000 个网站上的假 WordPress 插件提示用户安装恶意软件

黑客使用窃取的凭证感染 WordPress 网站&#xff0c;并向其发送虚假插件&#xff0c;通过虚假的浏览器更新提示向最终用户发送恶意软件和信息窃取程序。 该恶意活动基于ClickFix假浏览器更新恶意软件的新变种&#xff0c;自 2024 年 6 月以来已使用假 WordPress 插件感染了超过…

QT 机器视觉 1.相机类型

本专栏从实际需求场景出发详细还原、分别介绍大型工业化场景、专业实验室场景、自动化生产线场景、各种视觉检测物体场景介绍本专栏应用场景 更适合涉及到视觉相关工作者、包括但不限于一线操作人员、现场实施人员、项目相关维护人员&#xff0c;希望了解2D、3D相机视觉相关操作…

【问题解决】pnpm : 无法将“pnpm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

今天配置完poetry环境变量之后pnpm不能用了 具体报错 pnpm : 无法将“pnpm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&#xff0c;请确保路径正确&#xff0c;然后再试一次。 所在位置 行:1 字符: 1pnpm run dev~~~~ Ca…

【加密系统】华企盾DSC服务台提示:请升级服务器,否则可能导致客户端退回到旧服务器的版本

华企盾DSC服务台提示&#xff1a;请升级服务器&#xff0c;否则可能导致客户端退回到旧服务器的版本 产生的原因&#xff1a;控制台版本比服务器高导致控制台出现报错 解决方案 方法&#xff1a;将控制台回退到原来的使用版本&#xff0c;在控制台负载均衡查看连接该服务器各个…

若依框架部署到服务器后头像资源访问404

排错过程 第一开始以为是代理出问题了 官网给出的解决方案 第一种是用代理后端接口&#xff0c;第二种是重写路径直接访问静态文件 接口通过捕获profile开头的路径/profile/avatar…&#xff0c;转为/home…/avatar找到我们在该路径下的文件 但是我想了一下&#xff0c;我ngin…

基于Flink搭建流式湖仓OpenLake方案

OpenLake解决方案建立在开放可控的OpenLake湖仓之上&#xff0c;提供大数据搜索与AI一体化服务。通过元数据管理平台DLF管理结构化、半结构化和非结构化数据&#xff0c;提供湖仓数据表和文件的安全访问及IO加速&#xff0c;并支持大数据、搜索和AI多引擎对接。本文为您介绍以F…

windows下使用nvm进行多版本nodejs管理

目录 一&#xff1a;背景 二&#xff1a;nvm的介绍 三&#xff1a;环境切换使用 一&#xff1a;背景 最近在开发node js的项目&#xff0c;其中一个项目的前端和后台使用了两个node版本&#xff0c;因此需要不同的环境配置来进行开发任务&#xff0c;刚好nvm这个插件可以实现…