A Quick Overview of Hash Tables
Explore the foundational concepts of hash tables including their operations, handling collisions, and implementing them efficiently in JavaScript. Understand why hash tables offer constant time complexity on average and the challenges related to hash function optimization and memory usage.
We'll cover the following...
We'll cover the following...
Complete Implementation
Below, you can find the complete hash table class.
Node.js
class HashEntry {constructor(key, data) {this.key = key;// data to be storedthis.value = data;// reference to new entrythis.next = null;}}class HashTable {//Constructorconstructor() {//Size of the HashTablethis.slots = 10;//Current entries in the table//Used while resizing the table when half of the table gets filledthis.size = 0;//Array of HashEntry objects (by deafult all null)this.bucket = [];for (var i = 0; i < this.slots; i++) {this.bucket[i] = null;}this.threshold = 0.6}//Helper Functionsget_size() {return this.size;}//Hash FunctiongetIndex(key) {let index = key % this.slots;return index;}isEmpty() {return this.get_size() == 0;}insert(key, value) {//Find the node with the given keylet b_Index = this.getIndex(key);if (this.bucket[b_Index] == null) {this.bucket[b_Index] = new HashEntry(key, value);console.log(String(key) + ", " + String(value) + " - inserted.")} else {let head = this.bucket[b_Index];while (head != null) {if (head.key == key) {head.value = value;break;} else if (head.next == null) {head.next = new HashEntry(key, value);console.log(String(key) + ", " + String(value) + " - inserted.");break;}head = head.next;}}this.size += 1;let load_factor = Number(this.size) / Number(this.slots);//Checks if 60% of the entries in table are filled, threshold = 0.6if (load_factor >= this.threshold) {this.resize();}}//Return a value for a given keysearch(key) {//Find the node with the given keylet b_Index = this.getIndex(key);let head = this.bucket[b_Index]//Search key in the slotsif (head != null) {while (head != null) {if (head.key == key) {return head.value;}head = head.next}}//If key not foundconsole.log("Key not found");return null;}//Remove a value based on a keydeleteVal(key) {//Find indexlet b_Index = this.getIndex(key);let head = this.bucket[b_Index];//If key exists at first slotif (head.key == key) {this.bucket[b_Index] = head.next;console.log("Key deleted");this.size -= 1;return this;}//Find the key in slotslet prev = null;while (head != null) {//If key existsif (head.key == key) {prev.next = head.next;console.log("Key deleted");this.size -= 1;return this;}//Else keep moving in chainprev = head;head = head.next;}//If key does not existconsole.log("Key not found");return null;}resize() {let new_slots = this.slots * 2;let new_bucket = [];for (var n = 0; n < new_slots; n++) {new_bucket[n] = null;}this.slots = new_slots;// rehash all items into new slotsfor (var i = 0; i < this.bucket.length; i++) {let head = this.bucket[i];while (head != null) {let new_index = this.getIndex(head.key);if (new_bucket[new_index] == null) {new_bucket[new_index] = new HashEntry(head.key, head.value);} else {let node = new_bucket[new_index];while (node != null) {if (node.key == head.key) {node.value = head.value;node = null;} else if (node.next == null) {node.next = new HashEntry(head.key, head.value);node = null;} else {node = node.next;}}}head = head.next}}this.bucket = new_bucket;}}let table = new HashTable(); //Create a HashTableconsole.log(table.isEmpty());table.insert("This", 1);table.insert("is", 2)table.insert("a", 3)table.insert("Test", 4)table.insert("Driver", 5)console.log("Table Size: " + String(table.get_size()));console.log("The value for 'is' key: " + String(table.search("is")));table.deleteVal("is");table.deleteVal("a");console.log("Table Size: " + String(table.get_size()));
Summing Up
To sum up the discussion here, hash tables are the ideal ...