字典
存储[键,值]对
然后,我们需要声明一些映射/字典所能使用的方法。
- set(key,value):向字典中添加新元素。
- remove(key):通过使用键值来从字典中移除键值对应的数据值。
- has(key):如果某个键值存在于这个字典中,则返回true,反之则返回false。
- get(key):通过键值查找特定的数值并返回。
- clear():将这个字典中的所有元素全部删除。
- size():返回字典所包含元素的数量。与数组的length属性类似。
- keys():将字典所包含的所有键名以数组形式返回。
- values():将字典所包含的所有数值以数组形式返回。
散列表
HashTable类,也叫HashMap类,是Dictionary类的一种散列表实现方式。
散列算法的作用是尽可能快地在数据结构中找到一个值。如果使用散列函数,就知道值的具体位置,因此能够快速检索到该值。
散列函数的作用是给定一个键值,然后返回值在表中的地址
然后,给类添加一些方法。我们给每个类实现三个基础的方法。
- put(key,value):向散列表增加一个新的项(也能更新散列表)。
- remove(key):根据键值从散列表中移除值
- get(key):返回根据键值检索到的特定的值
散列集合
还有一种叫作散列集合的实现。散列集合由一个集合构成,但是插入、
移除或获取元素时,使用的是散列函数。我们可以重用本章中实现的所有代码来实现散列集合,
不同之处在于,不再添加键值对,而是只插入值而没有键。例如,可以使用散列集合来存储所有
的英语单词(不包括它们的定义)。和集合相似,散列集合只存储唯一的不重复的值。
散列表键冲突
有时候,一些键会有相同的散列值。不同的值在散列表中对应相同位置的时候,我们称其为冲突。
一个表现良好的散列函数是由几个方面构成的:插入和检索元素的时间(即性能),当然也包括较低的冲突可能性。
更好的散列函数是djb2(社区方案)
- 分离链接
分离链接法包括为散列表的每一个位置创建一个链表并将元素存储在里面。它是解决冲突的
最简单的方法,但是它在HashTable实例之外还需要额外的存储空间。 - 线性探查
当想向表中某个位置加入一个新元素的时候,如果索引
为index的位置已经被占据了,就尝试index+1的位置。如果index+1的位置也被占据了,就尝试
index+2的位置,以此类推。 - 双散列法
散列表
/*
- put(key,value):向散列表增加一个新的项(也能更新散列表)。
- remove(key):根据键值从散列表中移除值。
- get(key):返回根据键值检索到的特定的值。 */
// 给定一个key参数,我们就能根据组成key的每个字符的ASCII码值的和得到一个数字,这个数字作为下标存储。
// lose-lose函数
function HashTable() {
let table = []
let loseloseHashCode = function(key) {
let hash = 0
for (let i = 0; i < key.length; i++) {
hash += key.charCodeAt(i)
}
return hash % 37
}
this.put = function(key, value) {
let position = loseloseHashCode(key)
console.log(position + '-' + key)
console.log(position)
table[position] = value
}
this.remove = function(key) {
table[loseloseHashCode(key)] = undefined
}
this.get = function(key) {
return table[loseloseHashCode(key)]
}
this.getTable = function(key) {
return table
}
}
// var hash = new HashTable();
// hash.put('Gandalf', 'gandalf@email.com');
// hash.put('John', 'johnsnow@email.com');
// hash.put('Tyrion', 'tyrion@email.com');
// console.dir(hash.getTable());
// console.log(hash.getTable().length);
// console.log(hash.get('Gandalf'));
// console.log(hash.get('Loiane'));
// hash.remove('Gandalf');
// console.log(hash.get('Gandalf'));
// 键冲突
var hash = new HashTable();
hash.put('Gandalf', 'gandalf@email.com');
hash.put('John', 'johnsnow@email.com');
hash.put('Tyrion', 'tyrion@email.com');
hash.put('Aaron', 'aaron@email.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', 'sue@email.com');
hash.put('Mindy', 'mindy@email.com');
hash.put('Paul', 'paul@email.com');
hash.put('Nathan', 'nathan@email.com');
console.dir(hash.getTable());
分离散列
let LinkedList = function() {
let Node = function (element) { // 辅助类,管理下个元素指针指向
this.element = element
this.next = null
}
let length = 0
let head = null // 从头部到尾部的链,最后为null
// 列表为空,添加的是第一个元素,或者列表不为空,向其追加元素
this.append = function (element) {
let node = new Node(element)
let current
if(head === null) {
head = node // node的next还是为null(1)
} else {
current = head
while(current.next) { // 第二次进入的时候,第一个进入的值的next为null,因(1),直接走(2)
current = current.next // 有下一个则替换为下一个,继续查找,直到到底,next为null
}
current.next = node // 追加元素(2)。列表最后一个节点的下一个元素始终是null。
}
length++ // 更新列表长度
}
this.toString = function () {
let current = head
let string = ''
while(current) {
string += current.element
current = current.next
}
return string
}
this.isEmpty = function () {
return length === 0
}
this.size = function () {
return length
}
this.getHead = function () {
return head
}
this.insert = function (position, element) {
if (position >= 0 && position <= length) {
let node = new Node(element)
let current = head
let index = 0
if(position === 0) { // 头部插入
node.next = current
head = node
} else {
while(index++ < position) {
previous = current // 记录插入的元素前面的元素
current = current.next // 当前指向插入的元素后面的元素
}
node.next = current // 插入的元素的下一个是后面的元素
previous.next = node // 插入的元素的上一个元素的下一个元素是插入的元素
}
length++
return true
} else {
return false
}
}
// 移除元素也有两种场景:第一种是移除第一个元素,第二种是移除第一个以外的任一元素。
// 我们要实现两种remove方法:第一种是从特定位置移除一个元素,第二种是根据元素的值移除元素
this.removeAt = function (position) {
// 边界判断
if(position > -1 && position < length) {
let current = head
let previous
let index = 0
if(position === 0) {
head = current.next // 指向下一个元素
} else {
while( index++ < position) {
previous = current // 记录删除的元素前面的元素
current = current.next // 当前指向删除的元素后面的元素
}
// previous.next 原本是指向current的,
// 现在将previous与current的下一项链接起来:跳过current,
// 从而移除了current
previous.next = current.next
}
length--
return current.element // 返回下一个元素的值
} else {
return null
}
}
this.indexOf = function (element) {
let current = head
let index = 0
while(current) {
if(element === current.element) {
return index
}
index++
current = current.next
}
return -1
}
this.remove = function (element) {
let index = this.indexOf(element)
return this.removeAt(index)
}
}
// 分离链接
function HashTable() {
let table = []
let ValuePair = function(key, value) {
this.key = key
this.value = value
this.toString = function(){
return `[${this.key}-${this.value}]`
}
}
let loseloseHashCode = function(key) {
let hash = 0
for (let i = 0; i < key.length; i++) {
hash += key.charCodeAt(i)
}
return hash % 37
}
this.put = function(key, value) {
let position = loseloseHashCode(key)
if(!table[position]) {
table[position] = new LinkedList()
}
table[position].append(new ValuePair(key, value))
}
this.remove = function(key) {
let position = loseloseHashCode[key]
if(table[position] !== undefined) {
let current = table[position].head
while(current.next) {
if(current.element.key === key) {
table[position].remove(current.element)
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
}
this.get = function(key) {
let position = loseloseHashCode[key]
if(table[position] !== undefined) {
let current = table[position].head
while(current.next) {
if(current.element.key === key) {
return current.element.value
}
current = current.next
}
if(current.element.key === key) {
return current.element.value
}
}
return undefined
}
this.print = function(key) {
console.dir(table)
let tables = table.filter(item=>item)
console.log(tables)
for (let i = 0; i < tables.length; i++) {
console.log(`${i}-${tables[i].toString()}`)
}
}
}
// 键冲突
var hash = new HashTable();
hash.put('Gandalf', 'gandalf@email.com');
hash.put('John', 'johnsnow@email.com');
hash.put('Tyrion', 'tyrion@email.com');
hash.put('Aaron', 'aaron@email.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', 'sue@email.com');
hash.put('Mindy', 'mindy@email.com');
hash.put('Paul', 'paul@email.com');
hash.put('Nathan', 'nathan@email.com');
hash.print()
线性探查
// 线性探查
/* 。当想向表中某个位置加入一个新元素的时候,如果索引
为index的位置已经被占据了,就尝试index+1的位置。如果index+1的位置也被占据了,就尝试
index+2的位置,以此类推。 */
function HashTable() {
let table = []
let ValuePair = function(key, value) {
this.key = key
this.value = value
this.toString = function(){
return `[${this.key}-${this.value}]`
}
}
let loseloseHashCode = function(key) {
let hash = 0
for (let i = 0; i < key.length; i++) {
hash += key.charCodeAt(i)
}
return hash % 37
}
this.put = function(key, value) {
let position = loseloseHashCode(key)
if(!table[position]) {
table[position] = new ValuePair(key, value)
} else{
let index = ++position
while(table[index] !== undefined) {
index++
}
table[index] = new ValuePair(key, value)
}
}
this.remove = function(key) {
let position = loseloseHashCode[key]
if(table[position] !== undefined) {
if(table[position].key === key) {
table[position] = undefined
return true
} else {
let index = ++position
// 找到键存在并且该键的key是当前key,则跳出。
while(table[index] === undefined || table[index].key !== key) {
index++
}
if(table[index].key === key) {
table[index] = undefined
return true
}
}
}
return false
}
this.get = function(key) {
let position = loseloseHashCode[key]
if(table[position] !== undefined) {
if(table[position].key === key) {
return table[position].value
} else {
let index = ++position
// 找到键存在并且该键的key是当前key,则跳出。
while(table[index] === undefined || table[index].key !== key) {
index++
}
if(table[index].key === key) {
return table[index].value
}
}
}
return undefined
}
this.print = function(key) {
console.dir(table)
let tables = table.filter(item=>item)
console.log(tables)
for (let i = 0; i < tables.length; i++) {
console.log(`${i}-${tables[i].toString()}`)
}
}
}
// 键冲突
var hash = new HashTable();
hash.put('Gandalf', 'gandalf@email.com');
hash.put('John', 'johnsnow@email.com');
hash.put('Tyrion', 'tyrion@email.com');
hash.put('Aaron', 'aaron@email.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', 'sue@email.com');
hash.put('Mindy', 'mindy@email.com');
hash.put('Paul', 'paul@email.com');
hash.put('Nathan', 'nathan@email.com');
hash.print()
djb2(社区推荐)
function HashTable() {
let table = []
let djb2HashCode = function(key) {
let hash = 5318 // 括初始化一个hash变量并赋值为一个质数
for (let i = 0; i < key.length; i++) {
hash += hash * 33 + key.charCodeAt(i) // hash * 33 (用来当作一个魔力数
}
return hash % 1013 // 另一个随机质数(比我们认为的散列表的大小要大——在本例中,我们认为散列表的大小为1000)相除的余数
}
this.put = function(key, value) {
let position = djb2HashCode(key)
console.log(position + '-' + key)
console.log(position)
table[position] = value
}
this.remove = function(key) {
table[djb2HashCode(key)] = undefined
}
this.get = function(key) {
return table[djb2HashCode(key)]
}
this.getTable = function(key) {
return table
}
}
// 键冲突
var hash = new HashTable();
hash.put('Gandalf', 'gandalf@email.com');
hash.put('John', 'johnsnow@email.com');
hash.put('Tyrion', 'tyrion@email.com');
hash.put('Aaron', 'aaron@email.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', 'sue@email.com');
hash.put('Mindy', 'mindy@email.com');
hash.put('Paul', 'paul@email.com');
hash.put('Nathan', 'nathan@email.com');
console.dir(hash.getTable());