js data structure and algorithm dictionary and hash table

Dictionaries

Store [key, value] pairs
Then, we need to declare some methods that can be used by the mapping / dictionary.

  • set(key,value): adds a new element to the dictionary.
  • remove(key): remove the data value corresponding to the key value from the dictionary by using the key value.
  • has(key): if a key value exists in this dictionary, it returns true; otherwise, it returns false.
  • get(key): find a specific value through the key value and return it.
  • clear(): delete all the elements in this dictionary.
  • size(): returns the number of elements contained in the dictionary. Similar to the length attribute of an array.
  • keys(): returns all the key names contained in the dictionary as an array.
  • values(): returns all values contained in the dictionary as an array.

Hash table

HashTable class, also known as HashMap class, is a hash table implementation of Dictionary class.
The function of hash algorithm is to find a value in the data structure as soon as possible. If you use a hash function, you know the exact location of the value, so you can quickly retrieve the value.
The hash function gives a key value and returns the address of the value in the table

Then, add some methods to the class. We implement three basic methods for each class.

  • put(key,value): adds a new item to the hash table (it can also update the hash table).
  • remove(key): removes the value from the hash table based on the key value
  • get(key): returns the specific value retrieved according to the key value

hash set

There is also an implementation called a hash set. A hash set consists of a set, but inserts
Hash functions are used when removing or getting elements. We can reuse all the code implemented in this chapter to implement the hash set,
The difference is that instead of adding key value pairs, only values are inserted without keys. For example, you can use a hash collection to store all data
English words (excluding their definitions). Like collections, hash collections store only unique, non repeating values.

Hash table key conflict

Sometimes, some keys have the same hash value. When different values correspond to the same position in the hash table, we call it conflict.

A well performing hash function consists of several aspects: the time to insert and retrieve elements (i.e. performance), and of course, the low possibility of conflict.
A better hash function is djb2 (community scenario)

  1. Detach link
    The separate link method includes creating a linked list for each position of the hash table and storing the elements in it. It is the of conflict resolution
    The simplest method, but it requires additional storage space in addition to the HashTable instance.
  2. Linear exploration
    When you want to add a new element to a position in the table, if the index
    If the position of index has been occupied, try the position of index+1. If the position of index+1 is also occupied, try
    index+2, and so on.
  3. Double hash method

Hash table

/*  
      - put(key,value): Add a new item to the hash table (you can also update the hash table).
      - remove(key): Removes a value from the hash table based on the key value.
      - get(key): Returns a specific value retrieved from a key value. */
      // Given a key parameter, we can get a number according to the sum of ASCII code values of each character constituting the key, which is stored as a subscript.
      // Lose lose function
      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'));
      
      // Key Conflict
      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());

Split hash

let LinkedList = function() {
          let Node = function (element) { // Auxiliary class, which manages the pointer of the next element
            this.element = element
            this.next = null
          }
          let length = 0
          let head = null // The chain from head to tail, and finally null
          // If the list is empty, the first element is added, or if the list is not empty, an element is appended to it
          this.append = function (element) {
            let node = new Node(element)
            let current
            if(head === null) {
              head = node // node's next is still null (1)
            } else {
              current = head
              while(current.next) { // In the second entry, the next value of the first entry is null, because (1), go directly (2)
                current = current.next // If there is a next one, replace it with the next one. Continue to search until the end. Next is null
              }
              current.next = node // Append element (2). The next element of the last node in the list is always null.
            }
            length++ // Update list 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) { // Head insertion
                node.next = current
                head = node
              } else {
                while(index++ < position) {
                  previous = current // Record the element before the inserted element
                  current = current.next // Currently points to the element after the inserted element
                }
                node.next = current // The next inserted element is the following element
                previous.next = node // The next element of the previous element of the inserted element is the inserted element
              }
              length++
              return true
            } else {
              return false
            }
          }
          // There are also two scenarios for removing elements: the first is to remove the first element, and the second is to remove any element other than the first.
          // We want to implement two remove methods: the first is to remove an element from a specific location, and the second is to remove the element according to the value of the element
          this.removeAt = function (position) {
            // Boundary judgment
            if(position > -1 && position < length) {
              let current = head
              let previous
              let index = 0 
              if(position === 0) {
                head = current.next // Point to the next element
              } else {
                while( index++ < position) {
                  previous = current // Record the element before the deleted element
                  current = current.next // Currently points to the element after the deleted element
                }
                // previous.next originally pointed to current,
                // Now link previous to the next item of current: skip current,
                // To remove current
                previous.next = current.next
              }
              length--
              return current.element // Returns the value of the next 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)
          }
        }
      // Detach link
      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()}`)
          }
        }
      }
      // Key Conflict
      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()
      

Linear exploration

// Linear exploration
      /* . When you want to add a new element to a position in the table, if the index
 If the position of index has been occupied, try the position of index+1. If the position of index+1 is also occupied, try
index+2 And so on. */
      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
              // If the key exists and the key of the key is the current key, the system will pop up.
              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
              // If the key exists and the key of the key is the current key, the system will pop up.
              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()}`)
          }
        }
      }
      // Key Conflict
      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 (recommended by the community)

function HashTable() {
        let table = []
        let djb2HashCode = function(key) {
          let hash = 5318 // Including initializing a hash variable and assigning it to a prime number
          for (let i = 0; i < key.length; i++) {
            hash += hash * 33 + key.charCodeAt(i) // hash * 33 (used as a magic number)
          }
          return hash % 1013 // The remainder of the division of another random prime number (larger than we think the hash table is - in this case, we think the hash table is 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
        }
      }
      // Key Conflict
      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());
      

Tags: Javascript

Posted on Wed, 01 Dec 2021 10:46:40 -0500 by bibby