[Estructura de datos JS y algoritmo] Encapsulación de tabla hash y métodos de operación relacionados

Tabla de contenido

Uno, la premisa del método de operación de la tabla hash.

Dos, la encapsulación de la tabla hash

Tres, mejora de la eficiencia

Cuarto, se realiza el método.

1. Agregue el elemento put (clave, valor) a la tabla hash.

2. Obtener el valor de un elemento get (clave).

3. Eliminar operación eliminar (tecla)

4. Otros métodos

5. Prueba de código

Cinco, el código completo


Uno, la premisa del método de operación de la tabla hash.

Si aún no sabe qué es una tabla hash, puede leer la publicación del blog: Conocer las tablas hash e implementar funciones hash . Aquí está el método de dirección en cadena .

Dos, la encapsulación de la tabla hash

En la estructura de datos JS, la encapsulación de HashTable define tres atributos:

  •  La esencia de esta tabla hash de almacenamiento es una matriz, que se utiliza para almacenar datos.
  •  this.count   registra cuántos datos se han almacenado en la tabla hash actual.
  • La  longitud total de la matriz this.limit , que registra cuántos elementos se pueden almacenar en la matriz, que es un número primo.
function HashTable(){
      // 属性
      this.storage = [];
      this.count = 0;
      this.limit = 7;
 }

Antes de implementar varias operaciones, primero debemos usar el prototipo para presentar nuestra función hash encapsulada.

// 哈希函数
HashTable.prototype.hashFunc = function (str, size) {
  var hashCode = 0;

  for (var i = 0; i < str.length; i++) {
    hashCode = 37 * hashCode + str.charCodeAt();
  }

  var index = hashCode % size;

  return index;
}

Tres, mejora de la eficiencia

Nota:

La relación de recuento a límite se  convierte en el factor de llenado (elemento de datos total / longitud de la tabla hash).

  • En el método de dirección abierta, el factor de llenado máximo es 1, porque debe encontrar una posición en blanco para poder colocar el elemento. El factor de llenado del método de dirección en cadena puede ser mayor que 1, y puede insertar elementos infinitamente, pero cuando el factor de llenado es demasiado alto Cuando es grande, es decir, a medida que aumenta la cantidad de datos, el depósito correspondiente a cada índice será cada vez más largo. La eficiencia de nuestras operaciones de búsqueda y eliminación de inserción será muy baja. esta vez, necesitamos expandirnos .
  • Por supuesto, cuando se realiza la operación de eliminación, nuestros datos se vuelven cada vez menos. En este momento, el recuento se vuelve más pequeño y el factor de llenado se vuelve cada vez más pequeño, lo que también hará que la eficiencia sea menor. En este momento, necesitamos a encogerse .

¿Cómo expandirse y encogerse ?

Cuando se necesita expansión, la longitud de la matriz se duplica, pero como mencionamos anteriormente , el límite es la longitud total de la matriz, que registra cuántos elementos se pueden almacenar en la matriz, que es un número primo . Por lo tanto, necesitamos un algoritmo para obtener la longitud total de la matriz expandida, lo que implica juzgar y encontrar números primos . Para expandir la capacidad, la longitud de la matriz original * 2 se puede aumentar en 1 nuevamente para encontrar números primos. Para reducir la longitud de la matriz original / 2, agregue 1 nuevamente para encontrar un número primo.

El algoritmo es como sigue:

1. Determina si es un número primo

      // 判断是否为质数
      HashTable.prototype.isPrime = function (num) {
        var temp = parseInt(Math.sqrt(num));

        for (var i = 2; i <= temp; i++) {
          if (num % i == 0) return false;
        }

        return true;
      }

2. Obtener números primos

      // 获取质数的函数
      HashTable.prototype.getPrime = function (num) {
        while(!this.isPrime(num)){
          num++;
        }
        return num;
      }

3. Restaurar

      // 扩容和缩容的重新存储
      HashTable.prototype.resize = function (newLimit) {
        // 1、将旧的数组内容保存到oldStorage中
        var oldStorage = this.storage;

        // 2、重置所有的属性
        this.storage = [];
        this.count = 0;
        this.limit = 7;

        // 3、遍历oldStorage中的所有的桶bucket
        for (var i = 0; i < oldStorage.length; i++) {
            // 3.1取出对应的bucket
            var bucket = oldStorage[i];

            // 3.2判断bucket是否为空
            if (bucket == null) continue;

            // 3.3 bucket不为空,则将元素重新插入到新的数组中
            for (var j = 0; j < bucket.length; j++) {
              var tuple = bucket[j];
              this.put(tuple[0], tuple[1]);
            }
        }
      }

¿Cuándo necesito expandirme y encogerme ?

Cuando el elemento de datos total / longitud de la tabla hash = recuento / límite, es decir, cuando el factor de llenado es mayor que 0,75 , es necesario ampliar la capacidad .

Cuando la longitud de la tabla hash es superior a 7 y el factor de llenado es inferior a 0,25 , es necesario reducirla .

Cuarto, se realiza el método.

1. Agregue el elemento put (clave, valor) a la tabla hash.

Idea: 1. Obtenga el valor del índice de acuerdo con la clave y encuentre la posición correspondiente.
            2. Obtenga el cucharón correspondiente según la posición de índice correspondiente.

  •            1) Si el balde existe, insértelo o modifíquelo directamente.
  •            2) Si el depósito no existe, cree un depósito.

            3. Determine si la clave existe en el depósito.

  •           1) Modifique el valor del elemento si existe.
  •           2) Si no existe, recorra hasta el final e insértelo.

Código:

      HashTable.prototype.put = function (key, value) {

          // 1、根据key获取index值,找到相应的位置
          var index = this.hashFunc(key, this.limit);

          // 2、根据对应的index位置,获取对应的bucket
          var bucket = this.storage[index];

          // 如果桶不存在则创建桶
          if (bucket == null) {
            bucket = [];
            this.storage[index] = bucket;
          }

          // 3、判断是该桶中是否存在该key
          // 1)如果存在则修改元素的value值
          for (var i = 0; i < bucket.length; i++) {
            var tuple = bucket[i];
            if (tuple[0] == key) {
              tuple[1] = value;
              return ;
            }
          }

          // 2)如果不存在则遍历到最后将之插入
          bucket.push([key, value]);

          this.count += 1;
          
          // 4、判断是否需要扩容
          if (this.count > this.limit * 0.75) {
            var newSize = this.limit * 2;
            var newPrime = this.getPrime(newSize);
            this.resize(newPrime);
          }
      }

2. Obtener el valor de un elemento get (clave).

Ideas:

         1. Obtenga el valor del índice de acuerdo con la clave y busque la posición correspondiente.

          2. Obtenga el cucharón correspondiente según la posición de índice correspondiente.

  •            Si el depósito no existe, se devuelve un valor nulo.
  •            Si el depósito existe, recorra para averiguar si existe el valor clave.
  •                  Si existe, se devuelve el valor.
  •                  Devuelve nulo si no existe.

Código:

        // 获取操作
        HashTable.prototype.get = function (key) {

        // 1、根据key获取index值,找到相应的位置
        var index = this.hashFunc(key, this.limit);

        // 2、根据对应的index位置,获取对应的bucket
        var bucket = this.storage[index];


        // 如果桶不存在则返回null
        if (bucket == null) return null;

        // 如果桶存在,则遍历查找是否存在该key值,存在则返回value值
        for (var i = 0; i < bucket.length; i++) {
          var tuple = bucket[i];
          if (tuple[0] == key) {
            return tuple[1];
          }
        }

        // 不存在返回null
        return null;
      }

3. Eliminar operación eliminar (tecla)

Esta operación es muy similar al método de obtención.

 Ideas:

            1. Obtenga el valor del índice de acuerdo con la clave y encuentre la posición correspondiente
            2. Obtenga el cubo correspondiente de acuerdo con la posición del índice correspondiente

  •               Si el depósito no existe, devuelve un valor nulo.
  •               Si el depósito existe, recorra para averiguar si existe el valor clave.
  •                     Existe para eliminar la operación y devolver su valor
  •                     Devuelve nulo si no existe

Código:

      // 删除操作
       HashTable.prototype.remove = function (key) {
        // 1、根据key获取index值,找到相应的位置
        var index = this.hashFunc(key, this.limit);

        // 2、根据对应的index位置,获取对应的bucket
        var bucket = this.storage[index];


        // 如果桶不存在则返回null
        if (bucket == null) return null;


        // 如果桶存在,则遍历查找是否存在该key值,存在进行删除操作,并返回其值
        for (var i = 0; i < bucket.length; i++) {
          var tuple = bucket[i];
          if (tuple[0] == key) {
            bucket.splice(i, 1);
            this.count -= 1;

            // 3、判断是否需要缩容
            if (this.limit > 7 && this.count < this.limit * 0.25) {
              var newSize = Math.floor(this.limit / 2);
              var newPrime = this.getPrime(newSize);
              this.resize(newPrime);
            }

            return tuple[1];
          }
        }

        // 不存在返回null
        return null;
      }

4. Otros métodos

     // 判断哈希表是否为空
      HashTable.prototype.isEmpty = function () {
        return this.count == 0;
      }

      // 返回哈希表的长度
      HashTable.prototype.size = function () {
        return this.count;
      }

5. Prueba de código

    // 测试代码
    // 1、创建哈希表
    var ht = new HashTable();

    // 2、插入数据
    ht.put('abc', '123');
    ht.put('cba', '321');
    ht.put('hjg', '564');
    ht.put('kuo', '854');

    // 3、获取数据
    alert(ht.get('abc')); //123

    // 4、修改操作
    ht.put('abc', '222');
    alert(ht.get('abc'));  //222


    // 5、删除操作
    ht.remove('abc');
    alert(ht.get('abc'));  //null

Cinco, el código completo

<!DOCTYPE html>
<html>
<head>
  <title>哈希表相关的函数</title>
</head>
<body>

  <script type="text/javascript">
    function HashTable(){


      // 属性
      this.storage = [];
      this.count = 0;
      this.limit = 7;

      // 哈希函数
      HashTable.prototype.hashFunc = function (str, size) {
        var hashCode = 0;

        for (var i = 0; i < str.length; i++) {
          hashCode = 37 * hashCode + str.charCodeAt();
        }

        var index = hashCode % size;

        return index;
      }

      // 方法
      // 添加操作
      HashTable.prototype.put = function (key, value) {

        // 1、根据key获取index值,找到相应的位置
        // 2、根据对应的index位置,获取对应的bucket
          // 1)如果桶存在就直接插入或修改
          // 2)如果桶不存在则创建桶
        // 3、判断是该桶中是否存在该key
          // 1)如果存在则修改元素的value值
          // 2)如果不存在则遍历到最后将之插入

          // 1、根据key获取index值,找到相应的位置
          var index = this.hashFunc(key, this.limit);

          // 2、根据对应的index位置,获取对应的bucket
          var bucket = this.storage[index];

          // 如果桶不存在则创建桶
          if (bucket == null) {
            bucket = [];
            this.storage[index] = bucket;
          }

          // 3、判断是该桶中是否存在该key
          // 1)如果存在则修改元素的value值
          for (var i = 0; i < bucket.length; i++) {
            var tuple = bucket[i];
            if (tuple[0] == key) {
              tuple[1] = value;
              return ;
            }
          }

          // 2)如果不存在则遍历到最后将之插入
          bucket.push([key, value]);

          this.count += 1;
          
          // 4、判断是否需要扩容
          if (this.count > this.limit * 0.75) {
            var newSize = this.limit * 2;
            var newPrime = this.getPrime(newSize);
            this.resize(newPrime);
          }
      }

      // 获取操作
      HashTable.prototype.get = function (key) {

        // 1、根据key获取index值,找到相应的位置
        var index = this.hashFunc(key, this.limit);

        // 2、根据对应的index位置,获取对应的bucket
        var bucket = this.storage[index];


        // 如果桶不存在则返回null
        if (bucket == null) return null;

        // 如果桶存在,则遍历查找是否存在该key值,存在则返回value值
        for (var i = 0; i < bucket.length; i++) {
          var tuple = bucket[i];
          if (tuple[0] == key) {
            return tuple[1];
          }
        }

        // 不存在返回null
        return null;
      }

      // 删除操作
      HashTable.prototype.remove = function (key) {
        // 1、根据key获取index值,找到相应的位置
        var index = this.hashFunc(key, this.limit);

        // 2、根据对应的index位置,获取对应的bucket
        var bucket = this.storage[index];


        // 如果桶不存在则返回null
        if (bucket == null) return null;


        // 如果桶存在,则遍历查找是否存在该key值,存在进行删除操作,并返回其值
        for (var i = 0; i < bucket.length; i++) {
          var tuple = bucket[i];
          if (tuple[0] == key) {
            bucket.splice(i, 1);
            this.count -= 1;

            // 3、判断是否需要缩容
            if (this.limit > 7 && this.count < this.limit * 0.25) {
              var newSize = Math.floor(this.limit / 2);
              var newPrime = this.getPrime(newSize);
              this.resize(newPrime);
            }

            return tuple[1];
          }
        }

          // 不存在返回null
          return null;
      }

      // 其他方法
      // 判断哈希表是否为空
      HashTable.prototype.isEmpty = function () {
        return this.count == 0;
      }

      // 返回哈希表的长度
      HashTable.prototype.size = function () {
        return this.count;
      }

      // 判断是否为质数
      HashTable.prototype.isPrime = function (num) {
        var temp = parseInt(Math.sqrt(num));

        for (var i = 2; i <= temp; i++) {
          if (num % i == 0) return false;
        }

        return true;
      }

      // 获取质数的函数
      HashTable.prototype.getPrime = function (num) {
        while(!this.isPrime(num)){
          num++;
        }
        return num;
      }

      // 扩容和缩容的重新存储
      HashTable.prototype.resize = function (newLimit) {
        // 1、将旧的数组内容保存到oldStorage中
        var oldStorage = this.storage;

        // 2、重置所有的属性
        this.storage = [];
        this.count = 0;
        this.limit = 7;

        // 3、遍历oldStorage中的所有的桶bucket
        for (var i = 0; i < oldStorage.length; i++) {
            // 3.1取出对应的bucket
            var bucket = oldStorage[i];

            // 3.2判断bucket是否为空
            if (bucket == null) continue;

            // 3.3 bucket不为空,则将元素重新插入到新的数组中
            for (var j = 0; j < bucket.length; j++) {
              var tuple = bucket[j];
              this.put(tuple[0], tuple[1]);
            }
        }
      }
    }



    // 测试代码
    // 1、创建哈希表
    var ht = new HashTable();

    alert(ht.isPrime(11)); //true
    alert(ht.isPrime(123)); //false
    alert(ht.isPrime(17)); //true
    alert(ht.isPrime(9)); //false

    // 2、插入数据
    ht.put('abc', '123');
    ht.put('cba', '321');
    ht.put('hjg', '564');
    ht.put('kuo', '854');

    // 3、获取数据
    alert(ht.get('abc')); //123

    // 4、修改操作
    ht.put('abc', '222');
    alert(ht.get('abc'));  //222


    // 5、删除操作
    ht.remove('abc');
    alert(ht.get('abc'));  //null


  </script>

</body>
</html>

 

Supongo que te gusta

Origin blog.csdn.net/weixin_42339197/article/details/99630150
Recomendado
Clasificación