Using JavaScript to realize common data structure

Time:2020-2-29

By Paul Ryan

Crazy technology house

Original: https://blog.logrocket.com/kn

No reprint without permission

What are we going to talk about?

In JavaScript, data structures are often ignored or rarely touched. But for many large factories, you need to have a deep understanding of how to manage data. Mastering the data structure can also help you with your work when solving problems.

In this article, we will discuss and implement the data structure as follows:

  • Stack
  • queue
  • Linked list
  • Hashtable
  • tree

Stack

The first data structure is stack. It’s very similar to a queue, you’ve probably heard of it beforeCall stack, which is the method JavaScript uses to handle events.

The stack looks like this:

Using JavaScript to realize common data structure
The last item on the stack will be the first one to be removed. This is called LIFO. A good example is the back button in a web browser: add each page you view to the stack, and when you click back, the current page (the last page added) pops up from the stack.

There are enough theories. Let’s look at some code:

class Stack {
  constructor() {
    //Create the stack structure, which is an empty object
    this.stack = {}
  }
  //Push a value into the top of the stack
  push(value) {

  }
  //Pop the value at the top of the stack and return
  pop() {

  }

  //Reads the last value in the stack, but does not delete it
  peek() {

  }
}

I have commented on the above code, now let’s implement it together. The first way ispush

Think about what you need to do in this way:

  • We need to accept a value
  • Then add the value to the top of the stack
  • You should also track the length of the stack so that you know the index of the stack

If you can try it on your own, it’s great. It’s completepushThe method is as follows:

class Stack {
  constructor() {
    this._storage = {};
    This. _length = 0; // this is the stack size
  }

  push(value) {
    //Add value to top of stack
    this._storage[this._length] = value;
    //Since a value has been added, the length should also be increased by 1
    this._length++;
  }
  /// .....
}

I bet it’s easier than you think. There are many structures like this that sound much more complex than they actually are.

Now ispopMethod.popThe goal of the method is to delete the last value added to the stack and return it. If you can, please try to implement it yourself first:

class Stack {
  constructor() {
    this._storage = {};
    this._length = 0;
  }

  pop() {
    // we first get the last val so we have it to return
    const lastVal = this._storage[--this._length]
    // now remove the item which is the length - 1 
    delete this._storage[--this._length]
    // decrement the length
    this._length--;
    // now return the last value
    return lastVal
  }
}

Cool! It’s almost finished. The last one ispeekFunction that looks at the last item in the stack. This is the simplest feature: you only need to return the last value. The achievement is:

class Stack {
  constructor() {
    this._storage = {};
    this._length = 0;
  }
  /*
  * Adds a new value at the end of the stack
  * @param {*} value the value to push
  */

  peek() {
    const lastVal = this._storage[--this._length]
    return lastVal
  }
}

So it andpopThe method is very similar, but does not delete the last item.

Yes! The first data structure has been implemented. Then there is the queue, which is very similar to the stack.

queue

Next we’ll talk about queues – hopefully the stack will still be clear in your mind, because it’s very similar to queues. The main difference between stacks and queues is that queues are first in, first out (FIFO).

It can be represented graphically as follows:

Using JavaScript to realize common data structure

So the two main approaches areenqueueAnddequeue。 Data is added to the end of the team and removed from the head of the team. To better understand it, start to implement queues.

The core code structure is as follows:

class Queue {
  constructor() {
    //Like before, we provide an object for data structure
    //And there's a variable to hold the length
    this.queue = {}
    this.length = 0
    //This is a new variable to track the head
    this.head = 0
  }

  enqueue(value) {

  }

  dequeue() {

  }

  peek() {

  }
}

First realizeenqueueMethod. The goal is to add an item to the end of the team.

enqueue(value) {
  //Use the value parameter to add the key of length + head to the object
  this.queue[this.length + this.head] = value;
  this.length++
}

This is a very simple way to add a value at the end of the queue, but you maythis.queue[this.length + this.head] = value;Confused.

Suppose the queue looks like this{14 : 'randomVal'}。 When adding this content, the next value we want is15, so it should be length (1) + head (14), that is15

The next thing to do isdequeue

dequeue() {
  //Gets a reference to the first value to return
  const firstVal = this.queue[this.head]
  //Now remove it from the queue
  delete this.queue[this.head]
  this.length--;
  //And eventually add our head to the next node
  this.head++;
}

The last thing to do ispeekMethod, very simple:

peek() {
  //Just return the value
  return this.queue[this.head];
}

Queue implementation completed.

Linked list

Let’s talk about powerful lists first. This is much more complicated than the structure above.

Maybe your first question is why do you use linked lists? Linked list is mainly used for languages without dynamic resizing arrays. Linked lists organize items in order, with one item pointing to the next.

Each node in the list has onedataValue and onenextValue. In the following figure5yesdataValue,nextThe value points to the next node, that is, the value is10Node.

Visually, it looks like this:
Using JavaScript to realize common data structure

In an object, theLinkedListIt looks like the following

Using JavaScript to realize common data structure

You will see the last value1OfnextThe value isnullBecause this isLinkedListAt the end.

So how to achieve it?

Let’s create a value with12and37OfLinkedList

const myLinkedList = {
    head: {
        value: 1
        next: {
            value: 2           
            next: {
                value: 37
                next: null
            }
        }
    }
};

Now we know how to create it manuallyLinkedList, but coding is neededLinkedListMethod.

The first thing to note is,LinkedListJust a bunch of nested objects!

When constructing aLinkedListWe need aheadAnd onetail, they all point to the head at first (becauseheadIt’s the first and last one.

class LinkedList {
  constructor(value) {
    this.head = {value, next: null}
    this.tail = this.head
  }
}

The first way to do this is toinsert, which is used to insert a value at the end of the list.

//Insert will be added to the end of the list of links
insert(value) {
  /*Create a node*/
  const node = {value, next: null}
  /*Set the next property of tail to the reference of the new node*/
  this.tail.next = node;
  /*The new node is now the tail node*/
  this.tail = node;
}

The most confusing line up there might bethis.tail.next = node。 This is because when we add a new node, we also want the currenttailPointing to the newnode, the node will become a newtail。 First insertnodeOf the headnextThe pointer will point to the new node, as in the constructor, where thethis.tail = this.head

You can also go to this website to view the graphical presentation, which will help you understand the insertion process (pressescGet rid of annoying pop ups).

The next method is to delete the node. The first thing we have to decide is that the parameter is a value(value)Or to nodes(node)In an interview, it’s best to ask the interviewer first. We passed a “value” in our code. Removing nodes from the list by value is a slow process because you have to traverse the entire list to find the value.

I do this:

removeNode(val) {
  /*Start with head*/
  let currentNode = this.head
  /*We need to keep references to the previous node*/
  let previousNode
  /*When there is a node, it means that the tail is not reached*/
  while(currentNode) {
     /*If you find the value you want, exit the loop*/
     if(currentNode.value === val) {
        break;
     }
    /*Set currentnode to previousnode without finding a value*/
    previousNode = currentNode
    /*Get the next node and assign it to the currentnode*/
    currentNode = currentNode.next
  }
  /*Undefined returned because no node with this value was found*/
  if (currentNode=== null) {
    return false;
  }

  //If the node is head, set head to the next value
  Head node
  if (currentNode === this.head) {
    this.head = this.head.next;
    return;
  }
  /*Delete a node by setting it to the previous node*/  
  previousNode.next = currentNode.next
}

removeNodeHow to make usLinkedListWe have a good understanding of the way we work.

So again, let’s start with variablescurrentNodeSet toLinkedListOfheadBecause this is the first node. Then create a file namedpreviousNodePlaceholder variable that will bewhileUsed in a loop. Condition fromcurrentNodestartwhileLoop, as long as it existscurrentNode, it will run all the time.

staywhileThe first step in the loop is to check for values. If not, thepreviousNodeSet tocurrentNodeAnd willcurrentNodeSet to the next node in the list. Continue this process until you find the value I need to find or traverse the node.

staywhileAfter the loop, if notcurrentNodeThen returnfalse, which means no nodes were found. If there is onecurrentNode, thecurrentNodeWhether it ishead。 If so, putLinkedListOfheadSet to the second node, which will becomehead

Finally, ifcurrentNodeNot the head, justpreviousNodeSet to pointcurrentNodeAheadnode, which will be removed from the objectcurrentNode

Another common method (the interviewer may also ask you) isremoveTail。 This method is as it says, but it’s removedLinkedListThe tail node of. This is much easier than the above method, but it works in a similar way.

I suggest you try it on your own, and then look at the following code (to make it more complicated, we don’t use it in the constructortail):

removeTail() {
  let currentNode = this.head;
  let previousNode;
  while (currentNode) {
    /*Tail is the only node without the next value, so if there is no next value, then the node is tail*/
    if (!currentNode.next) {
      break;
    }
    //Get reference to previous node
    previousNode = currentNode;
    //Move to next node
    currentNode = currentNode.next;
  }
  //To remove the tail, set previousnode.next to null
  previousNode.next = null;
}

These areLinkedListSome of the main methods. There are various ways to link lists, but with the knowledge learned above, you should be able to implement them yourself.

Hashtable

Next comes a powerful hash table.

A hash table is a data structure that implements associative arrays, which means it maps keys to values. A JavaScript object is a “hash table” because it stores key value pairs.

Visually, it can be expressed as follows:

Using JavaScript to realize common data structure

Before we discuss how to implement hash tables, we need to discussThe importance of hash functions.The core concept of a hash function is that it accepts input of any size and returns a fixed length hash value.

hashThis('i want to hash this') => 7

Hash functions can be very complex or straightforward. Each file on GitHub is hashed, which makes the search of each file very fast. The core idea behind hash functions is that given the same input, the same output will be returned.

After introducing the hash function, it’s time to discuss how to implement the hash table.

The three actions to be discussed areinsertgetLast but not leastremove

The core code to implement the hash table is as follows:

class HashTable {
  constructor(size) {
    //Defines the size of the hash table, which will be used in the hash function
    this.size = size;
    this.storage = [];
  }
  insert(key, value) { }
  get() {}
  remove() {}
  //This is how the hash key is calculated
  myHashingFunction(str, n) {
    let sum = 0;
    for (let i = 0; i < str.length; i++) {
      sum += str.charCodeAt(i) * 3;
    }
    return sum % n;
  }
}

Now the first solution isinsertinsertThe code to the hash table is as follows (for simplicity, this method will simply handle conflicts):

insert(key, value) {
  //Get the index in the array
  const index = this.myHashingFunction(key, this.size);
  //Handling conflicts - if the hash function returns the same index for different keys,
  //In complex hash functions, conflicts are likely to occur
  if (!this.storage[index]) {
    this.storage[index] = [];
  }
  //Push new key value pair
  this.storage[index].push([key, value]);
}

Call like thisinsertMethod:

const myHT = new HashTable(5);
myHT.insert("a", 1);
myHT.insert("b", 2);

What do you think our hash table will look like?

Using JavaScript to realize common data structure

You can see that the key value pair is inserted into the index of the table1and4Place.

Now remove from hash table

remove(key) {
    //First, get the index of the key. Remember,
    //Hash function will always return the same index for the same key
    const index = this.myHashingFunction(key, this.size);
    //Remember that we can have multiple arrays at an index (unlikely)
    let arrayAtIndex = this.storage[index];
    if (arrayAtIndex) {
      //Traverse all arrays at this index
      for (let i = 0; i < arrayAtIndex.length; i++) {
        // get the pair (a, 1)
        let pair = arrayAtIndex[i];
        //Check whether the key matches the parameter key
        if (pair[0] === key) {
          delete arrayAtIndex[i];
          //Work is done, so exit the loop
          break;
        }
      }
    }
}

Last but not leastgetMethod. This sumremoveThe method is the same, but this time, let’s go backpairInstead of deleting it.

 get(key) {
    const index = this.myHashingFunction(key, this.size);
    let arrayAtIndex = this.storage[index];
    if (arrayAtIndex) {
      for (let i = 0; i < arrayAtIndex.length; i++) {
        const pair = arrayAtIndex[i];
        if (pair[0] === key) {
          return pair[1];
        }
      }
    }
  }

I don’t think it’s necessary to perform this operation because it’s related toremoveThe method is the same.

You can think of it as not as complicated as it initially seemed. This is a kind of data structure used everywhere, and also a well understood structure!

Binary search tree

The last data structure is the infamous binary search tree.

In a binary search tree, each node has zero, one, or two child nodes. The left is called the left child node, and the right is called the right child node. In a binary search tree, the children on the left must be smaller than the children on the right.

You can describe a binary search tree like this:

Using JavaScript to realize common data structure

The core classes of the tree are as follows:

class Tree {
   constructor(value) {
     this.root = null
   }

   add(value) {
    //We will implement it below
   }

}

We will also create aNodeClass to represent each node.

class Node {
  constructor(value, left = null, right = null) {
    this.value = value;
    this.left = left;
    this.right = right;
  }
}

Implementation belowaddMethod. I’ve commented on the code, but if you find it confusing, remember that all we have to do is start at the root and check each node’sleftandright

add(value) {
    //If there is no root, create a
    if (this.root === null) {
      this.root = new Node(value);
      return;
    }
    let current = this.root;
    // keep looping
    while (true) {
      //Left if the current value is greater than the passed in value
      if (current.value > value) {
        //If there are left child nodes, loop again
        if (current.left) {
          current = current.left;
        } else {
          current.left = new Node(value);
          return;
        }
      }
      //Less value, so we're on the right track
      else {
        // right
        //If there are left child nodes, run the loop again
        if (current.right) {
          current = current.right;
        } else {
          current.right = new Node(value);
          return;
        }
      }
    }
}

New testingaddMethod:

const t = new Tree();
t.add(2);
t.add(5);
t.add(3);

Now the tree looks like this:

Using JavaScript to realize common data structure

For better understanding, let’s implement a method to check whether the tree contains values.

contains(value) {
  //Get root node
  let current = this.root;
  //When nodes exist
  while (current) {
    //Check whether the current node is the value
    if (value === current.value) {
      Return true; // exit the function
    }
    //Determine the next current node by comparing our value with current.value
    //Left if small, right otherwise
    current = value < current.value ? current.left : current.right;
  }
  return false;
}

AddandContainsIt is the two core methods of binary search tree. Understanding these two methods can help you to solve problems in your daily work better.

summary

I’ve covered a lot in this article, and mastering this knowledge will put you in a good position for an interview. I hope you can learn something and pass the technical interview easily (especially the annoying white board interview).


This article starts with WeChat official account: front-end pioneer.

Welcome to scan the two-dimensional code to pay attention to the official account, and push you every day to send fresh front-end technical articles.

Using JavaScript to realize common data structure


Welcome to other great articles in this column:

  • Deep understanding of shadow DOM v1
  • Step by step to teach you how to use webvr to realize virtual reality games
  • 13 modern CSS frameworks to improve your development efficiency
  • Get started bootstrap Vue
  • How does the JavaScript engine work? Everything you need to know from call stack to promise
  • Websocket practice: real time communication between node and react
  • 20 interview questions about Git
  • In depth analysis of console.log of node.js
  • What is node.js?
  • Build an API server with node.js in 30 minutes
  • Object copy of JavaScript
  • Programmers can’t earn 30K a month before they are 30 years old. Where to go
  • 14 best JavaScript data visualization Libraries
  • 8 top level vs code extensions for the front end
  • Node.js multithreading full guide
  • Four schemes and implementation of transforming HTML into PDF

  • More articles