这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」。
我个人觉得要想进大厂,就必须学习了解算法和数据结构,经历了多次面试之后,也知道了算法和数据结构的重要性,所以,我也打算从今天开始,进入学习,下载了一下电子书,边看书,边跟着书的例子来敲一下代码,下面就开始总结一下对算法和数据结构的学习吧。
第二十四天:继续学习散列表
解决散列表的冲突
会有相同键的时候,不同的值在散列表中对应相同的位置的时候,就会冲突。例如下面的代码
const hash = new HashTable();
hash.put('Ygritte', 'ygritte@email.com');
hash.put('Jonathan', 'jonathan@email.com');
hash.put('Jamie', 'jamie@email.com');
hash.put('Jack', 'jack@email.com');
hash.put('Jasmine', 'jasmine@email.com');
hash.put('Jake', 'jake@email.com');
hash.put('Nathan', 'nathan@email.com');
hash.put('Athelstan', 'athelstan@email.com');
hash.put('Sue', 'sue@email.com');
hash.put('Aethelwulf', 'aethelwulf@email.com');
hash.put('Sargeras', 'sargeras@email.com');
通过对每个提到的名字调用 hash.hashCode 方法,输出结果如下。 4 - Ygritte 5 - Jonathan 5 - Jamie 7 - Jack 8 - Jasmine 9 - Jake 10 - Nathan 7 - Athelstan 5 - Sue 5 - Aethelwulf 10 - Sargeras
-
toString
为了获取上面代码执行的结果,我们实现一下toString方法
toString() { if(this.isEmpty()) return '' const keys = Object.keys(this.table) let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}` for(let i = 1; i < keys.length; i++) { objString = `${objString},{${keys[i]} => ${this.table[keys[i]].toString()}}`; } return objString }调用这个toString方法之后,获得下面的输出
{4 => [#Ygritte: ygritte@email.com]}
{5 => [#Aethelwulf: aethelwulf@email.com]}
{7 => [#Athelstan: athelstan@email.com]}
{8 => [#Jasmine: jasmine@email.com]}
{9 => [#Jake: jake@email.com]}
{10 => [#Sargeras: sargeras@email.com]}
由于有几个key的散列值相同,导致了冲突,下一个推进去的值覆盖了上一个值。既然有冲突,那么就需要有解决冲突的方法,解决冲突有几种方法:分离链接、线性探查和双散列法。
分离链接
分离链接法需要结合链表,在table的每一个位置保存的都是一个链表结构,就像下图
重新创建一个基本结构
class HashTableSeparateChaining {
constructor(toStrFn = defaultToString) {
this.toStrFn = toStrFn
this.table = {}
}
}
我们来重新实现一下上面三个方法
-
put
put(key, value) { if(value !== null) { const position = this.hashCode(key) if(this.table[position] === null){ this.table[position] = new LinkedList() } this.table[position].push(new ValuePair(key,value)) return true } return false }第一次加入的时候,我们需要初始化一个链表的实例,如果不是第一次添加,那么需要在这个链表后面添加一个数据。
-
get
get(key) { const position = this.hashCode(key) const linkedList = this.table[position] if(linkedList !== null && !linkedList.isEmpty()) { let current = linkedList.getHead() while(current !== null) { if(current.element.key === key) { return current.element.value } current = current.next } } return undefined }首先需要知道table有没有这个值,同时需要知道当前链表是不是空的,如果不是,则直接返回undefied,如果有,则需要遍历table这个值的链表,找到头部,while循环遍历下去。如果key值相同就返回这个值
-
remove
remove(key) { const position = this.hashCode(key) const linkedList = this.table[position] if(linkedList !== null && !linkedList.isEmpty()) { let current = linkedList.getHead(); while(current !== null) { if(current.element.key === key) { linkedList.remove(current.element) if(linkedList.isEmpty()) { delete this.table[position] } return true } current = current.next } } return false }remove方法和get方法查找的方法一样,当找到后,需要移除链表的值,移除完之后还需要知道链表空了没有,如果空了,那么久删除table的这个值