javascript learning data structure (binary tree, figure)

Posted by WLC135 on Sat, 18 Dec 2021 06:51:28 +0100

Binary tree and binary lookup tree

Tree is a nonlinear data structure, which stores data in a hierarchical manner. Trees are used to store hierarchical data, such as files in the file system; Trees are also used to store sequence tables.

Binary tree is a special tree with no more than two child nodes. Binary tree has some special computational properties, which makes some operations on them extremely efficient. Along a specific set of edges, it can go from one node to another node that is not directly connected to it. This group of edges from one node to another is called a path. The tree can be divided into several levels. The root node is level 0, its child node is level 1, the child node of the child node is level 2, and so on. The nodes of any layer in the tree can be regarded as the root of the subtree, which includes the child nodes of the root node, the child nodes of the child nodes, etc. We define the number of layers of the tree as the depth of the tree. Each node has a value related to it, which is sometimes called a key. The distance from one node to another is called a path

Each node of a binary tree cannot have more than two child nodes. By limiting the number of child nodes to 2, we can write an efficient program to insert, find and delete data in the tree. The two child nodes of a parent node are called left node and right node respectively. In some binary tree implementations, the left node contains a specific set of values and the right node contains another specific set of values.

When considering a special binary tree, such as binary lookup tree, it is very important to determine the child nodes.
Binary lookup tree is a special binary tree. Relatively small values are saved in the left node and larger values are saved in the right node. This feature makes the search efficient

The following implements a binary lookup tree

Definition of Node class

Each node contains three elements

  1. Key value data
  2. left node pointer
  3. right node pointer

    class Node{
      constructor(data, left, right){
        this.data = data;
        this.left = left;
        this.right = right;
      }
      show(){
        return this.data
      }
    }

Binary lookup tree class

Initialize root node root is null

class BST{
  root=null
  constructor(){
  }
}

The insert() method is used to add new nodes to the tree

  insert(data) {
        let node = new Node(data, null, null)
        if (this.root == null) {
            this.root = node
        } else {
            let current = this.root
            let parent
            while (true) {
                parent = current
                if (data < current.data) {
                    current = current.left
                    if (current == null) {
                        parent.left = node
                        break;
                    }
                } else {
                    current = current.right
                    if (current == null) {
                        parent.right = node
                        break;
                    }
                }
            }
        }
    }

Traversing binary search tree

There are three ways to traverse BST: middle order, first order and second order. Middle order traversal accesses all nodes on the BST in ascending order according to the key values on the nodes. The first order traversal first accesses the root node, and then accesses the left and right subtrees in the same way. The subsequent traversal first accesses the leaf node, from the left subtree to the right subtree, and then to the root node.

preOrder()

preOrder(node) {
        if (node != null) {
            console.log(node.show())
            this.inOrder(node.left)
            this.inOrder(node.right)
        }
    }

Middle order traversal inOrder()

  inOrder(node) {
        if (node != null) {
            this.inOrder(node.left)
            console.log(node.show())
            this.inOrder(node.right)
        }
    }

Post order traversal ()

postOrder(node) {
        if (node != null) {
            this.inOrder(node.left)
            this.inOrder(node.right)
            console.log(node.show())
        }
    }

getMin() finds the minimum value

The minimum value is on the far left. Uniformly traverse the left elements and find the last element, which is the minimum value

getMin() {
        let node = this.root
        while (node.left != null) {
            node = node.left
        }
        return node.data
    }

getMax() finds the maximum value

The maximum value is on the far right. Uniformly traverse the right elements and find the last element, which is the maximum value

getMax() {
        let node = this.root
        while (node.right != null) {
            node = node.right
        }
        return node.data
    }

find() finds the node with the specified value

find(data) {
        let node = this.root
        let result = null
        while (node) {
            if (node.data == data) {
                result = node
                break
            }
            if (node.data < data) {
                node = node.right
            } else {
                node = node.left
            }
        }
        

Delete node from binary lookup tree

Deleting a node on the BST is the most complex operation, and its complexity depends on which node is deleted. If you delete a node without child nodes, it is very simple. You can directly remove the node. If the node has only one child node, whether it is a left child node or a right child node, it becomes a little complicated. You need to point the pointer of the parent node to the deleted node to the corresponding left child node and the live right child node. Deleting a node containing two child nodes is the most complex. You need to replace the smallest node in the right subtree or the smallest node in the left subtree.
To manage the complexity of the delete operation, we use recursive operations and define two methods at the same time: remove() and removeNode().

remove(data) {
        this.root = this.removeNode(this.root, data)
    }

    removeNode(node, data) {
        if (node == null) {
            return null
        }
        if (node.data == data) {
            if (node.left == null && node.right == null) {
                return null
            }
            if (node.left == null) {
                return node.right
            }
            if (node.right !== null) {
                return node.left
            }
            let smallestNode = this.getSmallest(node.right)
            node.data = smallestNode.data
            this.removeNode(node.right, smallestNode.data)
            return node
        } else if (node.data < data) {
            node.right = this.removeNode(node.right.data)
        } else {
            node.left = this.removeNode(node.left.data)
        }
    }
    getSmallest(node) {
        while (node.left != null) {
            node = node.left
        }
        return node
    }

Complete code

class Node {
    constructor(data, left, right) {
        this.data = data;
        this.left = left;
        this.right = right;
    }
    show() {
        return this.data
    }
}
class BST {
    root = null
    constructor() {
    }
    insert(data) {
        let node = new Node(data, null, null)
        if (this.root == null) {
            this.root = node
        } else {
            let current = this.root
            let parent
            while (true) {
                parent = current
                if (data < current.data) {
                    current = current.left
                    if (current == null) {
                        parent.left = node
                        break;
                    }
                } else {
                    current = current.right
                    if (current == null) {
                        parent.right = node
                        break;
                    }
                }
            }
        }
    }
    inOrder(node) {
        if (node != null) {
            this.inOrder(node.left)
            console.log(node.show())
            this.inOrder(node.right)
        }
    }
    preOrder(node) {
        if (node != null) {
            console.log(node.show())
            this.inOrder(node.left)
            this.inOrder(node.right)
        }
    }
    postOrder(node) {
        if (node != null) {
            this.inOrder(node.left)
            this.inOrder(node.right)
            console.log(node.show())
        }
    }
    getMin() {
        let node = this.root
        while (node.left != null) {
            node = node.left
        }
        return node.data
    }
    getMax() {
        let node = this.root
        while (node.right != null) {
            node = node.right
        }
        return node.data
    }
    find(data) {
        let node = this.root
        let result = null
        while (node) {
            if (node.data == data) {
                result = node
                break
            }
            if (node.data < data) {
                node = node.right
            } else {
                node = node.left
            }
        }
        return result
    }
    remove(data) {
        this.root = this.removeNode(this.root, data)
    }
    removeNode(node, data) {

        if (node == null) {
            return null
        }
        if (node.data == data) {
            if (node.left == null && node.right == null) {
                return null
            }
            if (node.left == null) {
                return node.right
            }
            if (node.right !== null) {
                return node.left
            }
            let smallestNode = this.getSmallest(node.right)
            node.data = smallestNode.data
            this.removeNode(node.right, smallestNode.data)
            return node
        } else if (node.data < data) {
            node.right = this.removeNode(node.right.data)
        } else {
            node.left = this.removeNode(node.left.data)
        }
    }
    getSmallest(node) {
        while (node.left != null) {
            node = node.left
        }
        return node
    }

}

chart

A graph consists of a set of edges and a set of vertices. Edges are defined by vertex pairs (v1,v2). v1 and v2 are the two vertices in the graph respectively. Vertices also have weights. If the vertex pairs of a graph are ordered, it can be called a directed graph. After sorting the vertex pairs in a directed graph, you can draw an arrow between the two vertices. A directed graph shows the flow direction of vertices.

If a graph is unordered, it is called an unordered graph

We call the method of representing the edges of a graph adjacency table or adjacency table array. In this method, edges are stored as an array of adjacent vertex lists of vertices, and the vertices are used as indexes.

The following implementation diagram class

Represents a vertex

class Vertex{
  constructor(label){
    this.label=label
  }
}

Construction diagram

class Graph{
  edges=0
  adj=[]
  vertices=0
  marked=[]
  edgeTo=[]
  constructor(v){
    this.vertices = v
    for(let index=0;index < this.vertices;index++){
      this.adj[index]=[]
      this.marked[index]=false
    }
  }
}

addEdge() add node

 addEdge(v,w){
    this.adj[v].push(w)
    this.adj[w].push(v)
    this.edges++
  }

The showGraph() function displays the graph by printing a list of all vertices and their adjacent vertices

showGraph() {
    for (var i = 0; i < this.vertices; ++i) {
     let resStr = `${i}-> `
      for (var j = 0; j < this.vertices; ++j ) {
        if (this.adj[i][j] != undefined) {
          resStr=resStr+`${this.adj[i][j]} `
        }
      }
      console.log(resStr)
    }
  }

Depth first search

Depth first search includes tracing from the starting vertex of a path until it reaches the last vertex, then tracing back, continuing to trace the next path until it reaches the last vertex, and so on until there is no path. This is not a search for a specific path, but a search to see which paths can be selected in the diagram

  dfs(v){
    this.marked[v]=true
    if(this.adj[v] != undefined){
      console.log("Visited vertex: " + v)
    }
    for(let key of this.adj[v]){
      if(!this.marked[key]){
        this.dfs(key)
      }
    }
  }

Breadth first search

Breadth first search starts with the first vertex and tries to access the vertex as close to it as possible. In essence, this search moves layer by layer on the graph. First, check the layer closest to the first vertex, and then gradually move down to the layer furthest from the starting vertex

  bfs(s){
    let queue = []
    this.marked[s] = true
    queue.push(s)
    while(queue.length>0){
      let v = queue.shift()
      if(v == undefined){
       console.log(`Visisted vertex: ${v}`)
      }
      for(let key of this.adj[v]){
        if(!this.marked[key]){
          this.edgeTo[key]=v
          this.marked[key] = true
          queue.push(key)
        }
      }
    }
  }

Complete code

class Vertex{
  constructor(label){
    this.label=label
  }
}
class Graph{
  edges=0
  adj=[]
  vertices=0
  marked=[]
  edgeTo=[]
  constructor(v){
    this.vertices = v
    for(let index=0;index < this.vertices;index++){
      this.adj[index]=[]
      this.marked[index]=false
    }
  }
  addEdge(v,w){
    this.adj[v].push(w)
    this.adj[w].push(v)
    this.edges++
  }
  showGraph() {
    for (var i = 0; i < this.vertices; ++i) {
     let resStr = `${i}-> `
  
      for (var j = 0; j < this.vertices; ++j ) {
        if (this.adj[i][j] != undefined) {
          resStr=resStr+`${this.adj[i][j]} `
        }
      }
      console.log(resStr)
    }
  }
  dfs(v){
    this.marked[v]=true
    if(this.adj[v] != undefined){
      console.log("Visited vertex: " + v)
    }
    for(let key of this.adj[v]){
      if(!this.marked[key]){
        this.dfs(key)
      }
    }
  }
  bfs(s){
    let queue = []
    this.marked[s] = true
    queue.push(s)
    while(queue.length>0){
      let v = queue.shift()
      if(v == undefined){
       console.log(`Visisted vertex: ${v}`)
      }
      for(let key of this.adj[v]){
        if(!this.marked[key]){
          this.edgeTo[key]=v
          this.marked[key] = true
          queue.push(key)
        }
      }
    }
  }
}

Topics: Javascript Front-end