原文:

  Javascript 中学习数据结构与算法。

概念:

  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)的更多相关文章

  1. 数据结构和算法 – 7.散列和 Hashtable 类

    7.1.散列函数 散列是一种常见的存储数据的技术,按照这种方式可以非常迅速地插入和取回数据.散列所采用的数据结构被称为是散列表.尽管散列表提供了快速地插入.删除.以及取回数据的操作,但是诸如查找最大值 ...

  2. js数据结构之hash散列的详细实现方法

    hash散列中需要确定key和value的唯一确定关系. hash散列便于快速的插入删除和修改,不便于查找最大值等其他操作 以下为字符和数字的hash散列: function HashTable () ...

  3. javascript数据结构与算法--散列

    一:javascript数据结构与算法--散列  一:什么是哈希表? 哈希表也叫散列表,是根据关键码值(key,value)而直接进行访问的数据结构,它是通过键码值映射到表中一个位置来访问记录的,散列 ...

  4. 算法与数据结构(十二) 散列(哈希)表的创建与查找(Swift版)

    散列表又称为哈希表(Hash Table), 是为了方便查找而生的数据结构.关于散列的表的解释,我想引用维基百科上的解释,如下所示: 散列表(Hash table,也叫哈希表),是根据键(Key)而直 ...

  5. JavaScript数据结构与算法-散列练习

    散列的实现 // 散列类 - 线性探测法 function HashTable () { this.table = new Array(137); this.values = []; this.sim ...

  6. JS中数据结构之散列表

    散列是一种常用的数据存储技术,散列后的数据可以快速地插入或取用.散列使用的数据 结构叫做散列表.在散列表上插入.删除和取用数据都非常快. 下面的散列表是基于数组进行设计的,数组的长度是预先设定的,如有 ...

  7. 《数据结构与算法分析——C语言描述》ADT实现(NO.05) : 散列(Hash)

    散列(Hash)是一种以常数复杂度实现查找功能的数据结构.它将一个关键词Key,通过某种映射(哈希函数)转化成索引值直接定位到相应位置. 实现散列有两个关键,一是哈希函数的选择,二是冲突的处理. 对于 ...

  8. PTA数据结构与算法题目集(中文) 7-43字符串关键字的散列映射 (25 分)

    PTA数据结构与算法题目集(中文)  7-43字符串关键字的散列映射 (25 分) 7-43 字符串关键字的散列映射 (25 分)   给定一系列由大写英文字母组成的字符串关键字和素数P,用移位法定义 ...

  9. PTA数据结构与算法题目集(中文) 7-42整型关键字的散列映射 (25 分)

    PTA数据结构与算法题目集(中文)  7-42整型关键字的散列映射 (25 分) 7-42 整型关键字的散列映射 (25 分)   给定一系列整型关键字和素数P,用除留余数法定义的散列函数将关键字映射 ...

随机推荐

  1. spring中的Filter使用

    https://blog.csdn.net/bibiwannbe/article/details/81302920

  2. Codeforces Round #555 (Div. 3) B. Long Number 【仔细读题】

    B. Long Number time limit per test 2 seconds memory limit per test 256 megabytes input standard inpu ...

  3. 计量经济与时间序列_ACF自相关与PACF偏自相关算法解析(Python,TB(交易开拓者))

    1   在时间序列中ACF图和PACF图是非常重要的两个概念,如果运用时间序列做建模.交易或者预测的话.这两个概念是必须的. 2   ACF和PACF分别为:自相关函数(系数)和偏自相关函数(系数). ...

  4. HTML5中的data-*属性

    data-* 属性包括两部分: 属性名不应该包含任何大写字母,并且在前缀 "data-" 之后必须有至少一个字符: 属性值可以是任意字符串: 注释:用户代理会完全忽略前缀为 &qu ...

  5. CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com/pkgs/main/win-64/repodata.json.bz2> Elapsed: -

    将C:\Users\<本机用户名>\.condarc文件修改为 channels: - http://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/ ...

  6. mysql字段值为null时排序问题

    -- DESC 降序时候默认null值排在后面.ASC升序时默认null值排在前面,可使用 IS NULL处理 ORDER BY score desc,gmPrice IS NULL,gmPrice, ...

  7. JavaMail读取收件箱退信邮件/分析邮件附件获取Message_Id

    需求描述:公司最近有个项目邮件通知功能,但是客户上传的邮件地址并不一定存在,以及其他的各种问题.所有希望发送通知后有个回执,及时发现地址存在问题的邮箱. 需求分析:经过分析JavaMail可以读取收件 ...

  8. 吴裕雄--天生自然C语言开发:字符串

    ] = {'H', 'e', 'l', 'l', 'o', '\0'}; char greeting[] = "Hello"; #include <stdio.h> i ...

  9. 什么是CDN

    1.什么是cdn    cdn全称是内容分发网络.其目的是让用户能够更快速的得到请求的数据.简单来讲,cdn就是用来加速的,他能让用户就近访问数据,这样就更更快的获取到需要的数据.举个例子,现在服务器 ...

  10. thinking in java学习笔记:14章 类型信息

    14.2 Class 对象 https://github.com/zhaojiatao/javase 1.什么是Class对象,Class对象是用来做什么的? Class对象是java程序用来创建类的 ...