学习js数据结构与算法-散列表(3)

109 阅读3分钟

这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」。

个人觉得要想进大厂,就必须学习了解算法和数据结构,经历了多次面试之后,也知道了算法和数据结构的重要性,所以,我也打算从今天开始,进入学习,下载了一下电子书,边看书,边跟着书的例子来敲一下代码,下面就开始总结一下对算法和数据结构的学习吧。

第二十五天:继续学习散列表

线性探测

线性,是因为它处理冲突的方法是将元素直接存储到表中,而不是单独的数据结构中。当发现需要为某个位置添加新元素时,结果这个位置已经有值时,就尝试position+1这个位置,如果一直有值,位置被占,就一直加1下去,直到找到一个空闲的位置,下面这张图可以解释过程。

![image-20220220231745039](/Users/admin/Library/Application Support/typora-user-images/image-20220220231745039.png)

移除

当我们从散列表中移除一个键值对的时候,只是移除了那个位置,当查找同位置的其他元素时就会找到一个空位置,导致算法错误。

第一种删除方法:软删除,每次删除后就在删除的位置做一个标记,但这经过多次删除之后,会慢慢的降低散列表的效率,下图可以看到软删除的过程

![image-20220220235458404](/Users/admin/Library/Application Support/typora-user-images/image-20220220235458404.png)

第二种删除方法:检查是否需要移动一个或多个元素到之前的位置,这种方法可以避免找到一个空位置。下图展示了这个过程。

![image-20220220235620847](/Users/admin/Library/Application Support/typora-user-images/image-20220220235620847.png)

现在就实现线性探测的三个方法

  • put

    put(key, value) {
      if(value !== null) {
        let position = this.hashCode(key)
        if(this.table[position] === null) {
          this.table[position] = new ValuePair(key, value)
        }else {
    			let index = position + 1
          while(this.table[index] !== null) {
            index++
          }
          this.table[index] = new ValuePair(key, value)
        }
        return true
      }  
      return false
    }
    

    首先,判断传入的值的合法性,然后就知道找到hash的位置,如果table该位置没值,table就在该位置添加值,如果有值,则将位置加一,直到table位置上没有值。

  • get

    get(key) {
      const position = this.hashCode(key)
      if(this.table[position].key === key) {
        return this.table[position].value
      }
      let index = position + 1
      while(this.table[index] !== null &&this.table[index].key !== key) {
        index++
      }
      if(this.table[index] !== null &&this.table[index].key === key) {
        return this.table[index].value
      }
      return undefined
    }
    

    首先找到这个key在table的位置,如果该位置的key等于传入的key,则返回table在该位置的值,否则的话,对该位置加一,进入while函数,如果key值一直不相同,就position一直加一,最后判断table在index位置的key值和传入的key想不相同,如果相同则直接返回该位置table的值。

  • remove

    remove(key) {
      const position = this.hashCode(key)
      if(this.table[position].key === key) {
        delete this.table[position]
        this.verifyRemoveSideEffect(key, position)
        return true
      }
      let index = position + 1
      while(this.table[index] !== null && this.table[index].key !== key) {
        index++
      }
      if(this.table[index] !== null && this.table[index].key === key) {
        delete this.table[index]
        this.verifyRemoveSideEffect(key, index)
        return true
      }
      return false
    }
    
    verifyRemoveSideEffect(key, removedPosition) {
      const hash = this.hashCode(key);
      let index = removedPosition + 1;
      while (this.table[index] !== null) {
        const posHash = this.hashCode(this.table[index].key)
        if(posHash <= hash || posHash <= removedPosition) {
           this.table[removedPosition] = this.table[index]
           delete this.table[index]
          	removedPosition = index
        }
    		index++
      }
    }
    

    移除的寻找方法和get的寻找方法差不多,找到后进行delete删除,但是我们不知道后面有没有一些元素是因为冲突了并处理放到了后面,所以我们需要一个验证方法,去将需要移动的元素进行移动。

    verifyRemoveSideEffect首先找到需要移除的key值,然后将已经删除的位置的值加一,进入while,找到下一个位置的key值应该在的位置,如果应该在的位置小于等于移除的key或者应该在的位置小于等于已经移除的位置,则将需要移动的元素放到已经移除的位置上。