HashTable类也叫HashMap类,是Dictionary类的一种散列表实现方式。算法
散列算法的做用是尽量快的在数据结构中找到一个值。数据结构
在以前的学习中,若是你想要得到数据结构中的一个值,须要遍历整个数据结构来找到它。若是使用散列函数,就能知道具体位置,也就可以快速找到该值。app
使用最多见的散列函数--‘lose lose’散列函数,简单的将每一个键值中的每一个字母的ASCII码值相加,将获得的散列值做为地址。函数
(键值)John (散列函数)74+111+104+110 (散列值)399 造成散列表学习
地址 | 数据键值对 |
---|---|
[399] | john/john@email.com |
... | |
[685] | gandalf/@email.com |
建立一个散列表this
function HashTable() { var table = []; }
实现一个散列函数,即将ASCII码值相加的方法。指针
var loseloseHashTable = function(key) { var hash = 0; for(var i = 0; i < key.length; i++) { hash += key.charCodeAt(i); } return hash % 37; //这里只是为了获得比较小的数值,随便除了一个数 }
实现put(key, value)
方法,向散列表中添加一个新的项。code
this.put = function (key, value) { var position = loseloseHashTable(key); //获得散列值,即位置 table[position] = value; };
实现get(key)
方法,返回根据键值检索到的特定的值。索引
this.get = function(key) { var position = loseloseHashTable(key); return table[position]; };
实现remove()
方法,从散列中移除键值对应的数据值。ip
this.remove = function(key) { table[loseloseHashTable(key)] = undefined; //没有数据占据的位置默认值为undefined };
具体使用方法这里就不赘述了,就是方法的调用。
假若有多个键值获得的散列值相等,那么后面的元素会覆盖前面前面相同散列值的元素,
怎么解决呢?
分离连接
分离连接法为散列表的每个位置建立一个链表并将元素存储在里面。
地址 | 链表存储数据 |
---|---|
[5] | [no1/no1.com]指针-> [no2/no2.com]指针-> [X]null |
在地址5上,链表实例上有两个元素no1.com和no2.com。
须要添加一个valuePair类,来表示将要加入链表的实例的元素。
var valuePair = function(key, value) { this.key = key; this.value = value; this.toString = function() { return `[${this.key} - ${this.value}]`; } }
重写一个put()
方法
this.put = function(key, value) { var position = loseloseHashTable(key); if(table[position] == undefined) { table[position] = new LinkedList(); //若是这个位置是第一次被加入元素,那么就初始化一个LinkedList实例 } table[position].append(new valuePair(key, value)); //链表实现的append方法添加一个valuePair实例。 };
重写一个get(key)
方法
this.get = function(key) { var position = loseloseHashTable(key); if(table[position] !== undefined) { //位置上有元素存在 //遍历链表来寻找键/值 var current = table[position].getHead(); while (current.next) { //这里遍历不到链表最后一个位置 if(current.element.key === key) { return current.element.value; //element属性是valuePair的实例,包含key和value属性 } current = current.next; } //检查元素在链表第一个或者最后一个的状况 if(current.element.key === key) { return current.element.value; } } return undefined; //位置上没有值 };
重写remove(key)
方法
this.remove = function(key) { var position = loseloseHashTable(key); if(table[position] !== undefined) { var current = table[position].getHead(); while (current.next) { if(current.element.key === key) { table[position].remove(current.element); //链表实现的remove方法 if(table[position].isEmpty()) { //删除元素以后判断链表是否变空 table[position] = undefined; } return true; } current = current.next; } //检查是不是第一个或者最后一个元素 if(current.element.key === key) { table[position].remove(current.element); if(table[position].isEmpty()) { table[position] = undefined; } return true; } } return false; }
线性探查
若是索引为index的位置已经被占据了,就尝试index+1的位置,以此类推。
5的位置被占据,就寻找6的位置,6的位置被占据,就找7,7没被占据就赋值(本应该在位置5上,可是线性探查变成了位置7)
实现put(key, value)
方法
this.put = function(key, value) { var position = loseloseHashTable(key); if(table[position] === undefined) { //这个位置没有被占据 table[position] = new valuePair(key, value); } else { var index = ++position; //寻找下一个位置 while(table[index] !== undefined) { //被占据继续寻找下一个位置 index ++; } table[index] = new valuePair(key, value); } };
实现get()
方法
this.get = function(key) { var position = loseloseHashTable(key); if(table[position] !== undefined) { if(table[position].key === key) { //举例位置5 return table[position].value; //记号1 } else { var index = ++position; while(table[index] !== undefined && (table[index] && table[index].key !== key)) { //该位置有元素可是不是要寻找的元素 index ++; //索引增长 } if(table[index] && table[index].key === key) { //确认正确性 return table[index].value; //找到7 //记号2 } } } return undefined; };
实现remove()
方法
只须要改变get方法的记号1和记号2的位置代码便可
改成table[index] = undefined
;
var djb2HashCode = function(key) { var hash = 5381; //初始化一个hash变量并赋值为一个质数5381 for(var i = 0; i < key.length; i++) { //遍历 hash = hash * 33 + key.charCodeAt(i); } return hash % 1013; }
相比来讲,冲突会变少不少~