[JS data structure and algorithm] Hash table encapsulation and related operation methods

table of Contents

One, the premise of the operation method of the hash table

Two, the encapsulation of the hash table

Three, efficiency improvement

Fourth, the method is realized.

1. Add the element put(key, value) to the hash table.

2. Get the value of an element get(key).

3. Delete operation remove(key)

4. Other methods

5. Code testing

Five, the complete code


One, the premise of the operation method of the hash table

If you still don’t know what a hash table is, you can read the blog post: Knowing Hash Tables and Implementing Hash Functions . Here is the chain address method .

Two, the encapsulation of the hash table

In the JS data structure, the encapsulation of HashTable defines three attributes:

  •  The essence of this.storage hash table is an array, which is used to store data.
  •  this.count   records how much data has been stored in the current hash table.
  • The  total length of the this.limit array, which records how many elements can be stored in the array, which is a prime number.
function HashTable(){
      // 属性
      this.storage = [];
      this.count = 0;
      this.limit = 7;
 }

Before implementing various operations, we first need to use the prototype to introduce our encapsulated hash function.

// 哈希函数
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;
}

Three, efficiency improvement

note:

The ratio of count to limit  becomes the filling factor (total data item/hash table length).

  • In the open address method, the maximum filling factor is 1, because it must find a blank position to put the element in. The filling factor of the chain address method may be greater than 1, and you can insert elements infinitely, but when the filling factor is too high When it is large, that is, as the amount of data increases, the bucket corresponding to each index will become longer and longer. The efficiency of our insert search and delete operations will become very low. At this time, we need to expand .
  • Of course, when the deletion operation is performed, our data becomes less and less. At this time, the count becomes smaller and the filling factor becomes smaller and smaller, which will also cause the efficiency to become lower. At this time, we need to shrink .

How to expand and shrink ?

When expansion is needed, the length of the array is doubled, but as we mentioned earlier , limit is the total length of the array, which records how many elements can be stored in the array, which is a prime number . Therefore, we need an algorithm to get the total length of the expanded array, which involves a judgment and finding prime numbers . To expand the capacity, the original array length *2 can be increased by 1 again to find prime numbers. To shrink the original array length/2, add 1 again to find a prime number.

The algorithm is as follows:

1. Determine whether it is a prime number

      // 判断是否为质数
      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. Get prime numbers

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

3. Restore

      // 扩容和缩容的重新存储
      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]);
            }
        }
      }

When do I need to expand and shrink ?

When the total data item / hash table length = count / limit, that is, when the filling factor is greater than 0.75 , the capacity needs to be expanded .

When the length of the hash table is greater than 7 and the filling factor is less than 0.25 , it needs to be reduced .

Fourth, the method is realized.

1. Add the element put(key, value) to the hash table.

Idea: 1. Get the index value according to the key and find the corresponding position.
            2. Obtain the corresponding bucket according to the corresponding index position.

  •            1) If the bucket exists, insert or modify it directly.
  •            2) If the bucket does not exist, create a bucket.

            3. Determine whether the key exists in the bucket.

  •           1) Modify the value of the element if it exists.
  •           2) If it does not exist, traverse to the end and insert it.

Code:

      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. Get the value of an element get(key).

Ideas:

         1. Get the index value according to the key and find the corresponding position.

          2. Obtain the corresponding bucket according to the corresponding index position.

  •            If the bucket does not exist, null is returned.
  •            If the bucket exists, traverse to find whether the key value exists.
  •                  If it exists, the value is returned.
  •                  Return null if it does not exist.

Code:

        // 获取操作
        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. Delete operation remove(key)

This operation is very similar to the get method.

 Ideas:

            1. Obtain the index value according to the key and find the corresponding position
            2. Obtain the corresponding bucket according to the corresponding index position

  •               If the bucket does not exist, return null
  •               If the bucket exists, traverse to find whether the key value exists.
  •                     Exist to delete operation and return its value
  •                     Returns null if it does not exist

Code:

      // 删除操作
       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. Other methods

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

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

5. Code testing

    // 测试代码
    // 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

Five, the complete code

<!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>

 

Guess you like

Origin blog.csdn.net/weixin_42339197/article/details/99630150