js 实现数据结构 -- 散列(HashTable)
原文:
概念:
HashTable 类, 也叫 HashMap 类,是 Dictionary 类的一种散列表实现方式。
散列算法的作用是尽可能快地在数据结构中定位到某个值,如之前的一些数据结构中(说的就是 LinkedList),需要遍历数据结构才能得到,如果使用散列函数,就能知道值的具体位置,因此就能快速检索到该值,单列函数的作用是给定一个键值(位置),然后返回值在表中的地址。
例如下例,我们通过使用 'lose lose' 散列函数(简单将每个键值中的每个字母 ASCII 值相加),获得一个散列表:
使用 js 实现如下:
class HashTable {
constructor() {
this.table = []; // 数组形式存储
} // 散列运算函数,可自定义
// 此处时最常见的散列函数 ‘lose lose’
static loseloseHashCode(key) {
let hash = 0;
for (let codePoint
key) {
hash += codePoint.charCodeAt();
}
return hash % 37;
} // 修改和增加元素
put(key, value) {
const position = HashTable.loseloseHashCode(key);
console.log(`${position} - ${key}`);
this.table[position] = value;
} get(key) {
return this.table[HashTable.loseloseHashCode(key)];
} remove(key) {
this.table[HashTable.loseloseHashCode(key)] = undefined;
}
} const hash = new HashTable();
hash.put('Surmon', 'surmon.me@email.com') // 15 - Surmon
hash.put('Jhon', 'Jhonsnow@email.com') // 29 - Jhon
hash.put('Tyrion', 'Tyrion@email.com') // 16 - Tyrion console.log(hash.get('Surmon')); // surmon.me@email.com
console.log(hash.get('Loiane')); // undefined
console.log(hash)
除散列表之外,还有散列映射和散列几何等散列结构。散列映射与散列表是一样的,而散列集合在插入、移除和获取元素的时候不再添加键值对,而是使用散列函数来代替 key。 和 集合相似,散列集合只存储唯一的不重复的值。
处理冲突:
散列表中有个头疼的问题是当一些键具有相同的散列值的时候,该如何处理这些冲突?比如:
const hash = new HashTable()
hash.put('Gandalf', 'gandalf@email.com')
hash.put('John', 'johnsnow®email.com')
hash.put('Tyrion', 'tyrion@email.com')
hash.put('Aaron', 'aaronOemail.com')
hash.put('Donnie', 'donnie@email.com')
hash.put('Ana', 'ana©email.com')
hash.put('Jonathan', 'jonathan@email.com')
hash.put('Jamie', 'jamie@email.com')
hash.put('Sue', 'sueOemail.com')
hash.put('Mindy', 'mindy@email.com')
hash.put('Paul', 'paul©email.com')
hash.put('Nathan', 'nathan@email.com')
栗子中,Tyrion 和 Aaron 有相同的散列值(16),Donnie 和 Ana 有相同的散列值(13),Jonathan、Jamie 和 Sue 有相同的散列值(5), Mindy 和 Paul 也有相同的散列值(32),导致最终的数据对象中,只有最后一次被添加/修改的数据会覆盖原本数据,进而生效。
处理冲突的几种方法:分离链接、线性查探和双散列法。下面是前两种方法:
分离链接:
分离链接的实质是在散列表的每一个位置创建一个链表并将元素存储,是解决冲入的最简单的方法,但是会在 HashTable 实例之外创建额外的存储空间。其示意图如下:
为了实现上述的分离链接,我们只需引入 LinkedList 类并修改 put、get 和 remove 这三个方法即可:
put(key, value) {
const position = HashTable.loseloseHashCode(key)
if (this.table[position] === undefined) {
this.table[position] = new LinkedList()
}
this.table[position].append({ key, value })
} get(key) {
const position = HashTable.loseloseHashCode(key)
if (this.table[position] === undefined) return undefined
const getElementValue = node => {
if (!node && !node.element) return undefined
if (Object.is(node.element.key, key)) {
return node.element.value
} else {
return getElementValue(node.next)
}
}
return getElementValue(this.table[position].head)
} remove(key) {
const position = HashTable.loseloseHashCode(key)
if (this.table[position] === undefined) return undefined
const getElementValue = node => {
if (!node && !node.element) return false
if (Object.is(node.element.key, key)) {
this.table[position].remove(node.element)
if (this.table[position].isEmpty) {
this.table[position] = undefined
}
return true
} else {
return getElementValue(node.next)
}
}
return getElementValue(this.table[position].head)
}
线性查探
线性查探的基本思路是,在插入元素时,如果索引为 index 的位置已经被占据,就尝试 index + 1 的位置,如果 index + 1 的位置也被占据,则尝试 index + 2 的位置,以此类推。如下图:
同样,线性查探只需要修改 put、get、remove 方法,不需要使用额外空间:
put(key, value) {
const position = HashTable.loseloseHashCode(key)
if (this.table[position] === undefined) {
this.table[position] = { key, value }
} else {
let index = ++position
while (this.table[index] !== undefined) {
index++
}
this.table[index] = { key, value }
}
this.table[position].append({ key, value }) // 这句没理解什么意思
} get(key) {
const position = HashTable.loseloseHashCode(key)
const getElementValue = index => {
if (this.table[index] === undefined) return undefined
if (Object.is(this.table[index].key, key)) { // 再根据 key 值是否相等判断是否是需要的元素
return this.table[index].value
} else {
return getElementValue(index + 1)
}
}
return getElementValue(position)
} remove(key) {
const position = HashTable.loseloseHashCode(key)
const removeElementValue = index => {
if (this.table[index] === undefined) return false
if (Object.is(this.table[index].key, key)) {
this.table[index] = undefined
return true
} else {
return removeElementValue(index + 1)
}
}
return removeElementValue(position)
}
从上例中我们可以看出,优化 HashTable 的要点在于散列函数。'lose lose' 散列函数并不是一个表现良好的散列函数,而网上也有其他的散列函数:djb2、sdbm.... 或者页可以实现自己的散列函数。下面是吸能良好的 djb2 函数:
static djb2HashCode(key) {
let hash = 5381
for (let codePoint of key) {
hash = hash * 33 + codePoint.charCodeAt()
}
return hash % 1013
}
js 实现数据结构 -- 散列(HashTable)的更多相关文章
- 数据结构和算法 – 7.散列和 Hashtable 类
7.1.散列函数 散列是一种常见的存储数据的技术,按照这种方式可以非常迅速地插入和取回数据.散列所采用的数据结构被称为是散列表.尽管散列表提供了快速地插入.删除.以及取回数据的操作,但是诸如查找最大值 ...
- js数据结构之hash散列的详细实现方法
hash散列中需要确定key和value的唯一确定关系. hash散列便于快速的插入删除和修改,不便于查找最大值等其他操作 以下为字符和数字的hash散列: function HashTable () ...
- javascript数据结构与算法--散列
一:javascript数据结构与算法--散列 一:什么是哈希表? 哈希表也叫散列表,是根据关键码值(key,value)而直接进行访问的数据结构,它是通过键码值映射到表中一个位置来访问记录的,散列 ...
- 算法与数据结构(十二) 散列(哈希)表的创建与查找(Swift版)
散列表又称为哈希表(Hash Table), 是为了方便查找而生的数据结构.关于散列的表的解释,我想引用维基百科上的解释,如下所示: 散列表(Hash table,也叫哈希表),是根据键(Key)而直 ...
- JavaScript数据结构与算法-散列练习
散列的实现 // 散列类 - 线性探测法 function HashTable () { this.table = new Array(137); this.values = []; this.sim ...
- JS中数据结构之散列表
散列是一种常用的数据存储技术,散列后的数据可以快速地插入或取用.散列使用的数据 结构叫做散列表.在散列表上插入.删除和取用数据都非常快. 下面的散列表是基于数组进行设计的,数组的长度是预先设定的,如有 ...
- 《数据结构与算法分析——C语言描述》ADT实现(NO.05) : 散列(Hash)
散列(Hash)是一种以常数复杂度实现查找功能的数据结构.它将一个关键词Key,通过某种映射(哈希函数)转化成索引值直接定位到相应位置. 实现散列有两个关键,一是哈希函数的选择,二是冲突的处理. 对于 ...
- PTA数据结构与算法题目集(中文) 7-43字符串关键字的散列映射 (25 分)
PTA数据结构与算法题目集(中文) 7-43字符串关键字的散列映射 (25 分) 7-43 字符串关键字的散列映射 (25 分) 给定一系列由大写英文字母组成的字符串关键字和素数P,用移位法定义 ...
- PTA数据结构与算法题目集(中文) 7-42整型关键字的散列映射 (25 分)
PTA数据结构与算法题目集(中文) 7-42整型关键字的散列映射 (25 分) 7-42 整型关键字的散列映射 (25 分) 给定一系列整型关键字和素数P,用除留余数法定义的散列函数将关键字映射 ...
随机推荐
- ArcGIS 二次开发总结
个人总结 1. Enterprise10.7新特性 新增共享实例,可以将不常用服务分配共享实例,减少服务器压力.仅支持ArcGIS pro发布的地图服务,且仅开启feature access,kml, ...
- log4j2和logback动态修改日志级别工具类
工作中,在排查线上问题时,有以下场景在不重新部署或重启服务的情况下,需要动态调整线上日志级别 1.线上有些日志打印过多干扰有用的日志,需要动态修改线上日志记录器的打印日志级别,调高一些日志级别,打印出 ...
- ZJNU 2226 - B.T.B.F
d为1代表Alice执刀,-1代表Bob执刀记录蛋糕数字为奇数和偶数各自的数量,先看偶数块 如果为偶数的蛋糕为奇数块,则为偶数的蛋糕切完后是Bob持刀,Alice会多吃1,此时要将奇数块蛋糕-1(假设 ...
- 个人训练记录(UPD 9.16)
本文章记录一些较难的题,摘自自己的blog中的其他文章.也有些单独成章有点浪费的题也写在里面了. 2019.7.15-2019.7.21 1182F(2900) 题意:求在区间 \([a,b]\) 中 ...
- linux服务器CentOS7安装node.js
方维一元夺宝2.0版本,很多用户面临机器人自动执行任务.采集计划一直无法开启的问题. 这个需要开启node.js分享给大家. 1.获取node.js资源 V5.x: curl --silent --l ...
- Python笔记_第一篇_面向过程_第一部分_9.Ubuntu基础操作
第一部分 Ubuntu简介 Ubuntu(乌班图)是一个机遇Debian的以桌面应用为主的Linux操作系统,据说其名称来自非洲南部祖鲁语或科萨语的“Ubuntu”一词,意思是“人性”.“我的存在 ...
- NSPredicate 应用1
//查找名字里面包含“王”的姓 NSArray *array = [[NSArray alloc]initWithObjects:@"小王",@"王力",@&q ...
- docker安装(centos-7)
centos7安装docker:Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的CentOS 版本是否支持 Docker .通过 uname -r 命令 ...
- 吴裕雄--天生自然python Google深度学习框架:图像识别与卷积神经网络
- 吴裕雄--天生自然 PYTHON3开发学习:数据库连接 - PyMySQL 驱动
import pymysql # 打开数据库连接 db = pymysql.connect("localhost","testuser","test1 ...