简介
散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构。
也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度。
这个映射函数称做散列函数,存放记录的数组称做散列表。
const table = []; // 记录的数组,即散列表// 散列函数const computeHashCode = function(key) {// 通过某种算法,算出数值 code// 算法可以是 md5、SHA-1 等// JS 中有 charCodeAt 方法可以获取字符的 unicode 编码,也可以作为一种算法。// ...return code; // 将数值返回}// 设置值const set = (key, value) => {const position = computeHashCode(key);table[position] = value;}
补充一下,散列表其实也可以是对象,只要算出的 hashCode 是字符串就可以。 在下文,我们以数组 和 JS 的 charCodeAt 来模拟散列表。
散列函数的特点
- 确定性
- 输出不同,输入肯定不同
 
 - 散列碰撞(散列冲突)
- 输入输出不一定一一对应,多个输入可以对应一个输出
 
 - 不可逆性
- 输出内容无法反推输入内容
 
 - 混淆特性
- 输入即使变化很小,输出的变化会很大
 
 
JS 实现散列表
普通散列表
class HashTable {table = [];// 散列函数static computeHashCode(key) {let code = 0;for (let i = 0; i < key.length; i++) {code += key.charCodeAt(i)}// 这里对 37 取余,只是为了配合下面举例,讲解散列碰撞// 换成其他的也行return code % 37;}// 设置值set(key, value) {const position = HashTable.computeHashCode(key);this.table[position] = value;}get(key) {const position = HashTable.computeHashCode(key);return this.table[position]}remove(key) {const position = HashTable.computeHashCode(key);delete this.table[position]}}const hashTable = new HashTable()// 添加值并打印输出的话可能会得到相同的hash值hashTable.set('Tyrion', 'aaa@qq.com') // position = 16hashTable.set('Aaron', 'bbb@qq.com') // position = 16
解决散列碰撞
链表法
使用链表来辅助存储。对于普通的散列表,本身在某个位置存储的是一个值。
而我们知道链表是有序的元素的集合,拥有存储元素本身的值和指向下一个值的引用,那么我们将某个位置本来要存储的值换成链表的形式,这就意味着我们在这个位置可以存放多个值了。
class Node {constructor(value, next) {this.value = value;this.next = next;}}class NodeList {constructor() {this.head = new Node(); // 哑节点this.tail = this.head}append(value, next) {const node = new Node(value, next);this.tail.next = node;this.tail = node;}getList() {return this.head;}}module.exports = NodeList;
const NodeList = require('./NodeList')class HashTable {table = [];// 散列函数static computeHashCode(key) {let code = 0;for (let i = 0; i < key.length; i++) {code += key.charCodeAt(i)}// 这里对 37 取余,只是为了配合下面举例,讲解散列碰撞// 换成其他的也行return code % 37;}// 设置值set(key, value) {const position = HashTable.computeHashCode(key);if (!this.table[position]) this.table[position] = new NodeList();this.table[position].append({ key, value });}get(key) {const position = HashTable.computeHashCode(key);let head = this.table[position].getList().next;while (head) {if (head.value.key === key) return head.value.value;head = head.next}}remove(key) {const position = HashTable.computeHashCode(key);let pre = this.table[position].getList();let p = pre.nextwhile (p) {if (p.value.key === key) {pre.next = p.next}pre = p;p = p.next}}}const hashTable = new HashTable()// 添加值并打印输出的话可能会得到相同的hash值hashTable.set('Tyrion', 'aaa@qq.com') // position = 16hashTable.set('Aaron', 'bbb@qq.com') // position = 16hashTable.get('Tyrion') // aaa@qq.comhashTable.get('Aaron') // bbb@qq.com
