Binary Search Tree

April 19, 202223 min read

DSA
JavaScript
Tree

What is Binary Search Tree (BST)

A binary search tree (BST) is a type of binary tree that is used for searching and sorting data. It is called a “search tree” because it can be efficiently used to search for the presence or absence of a particular element in the tree. It is called a “binary” tree because each node has at most two children.

In a BST, the left child of a node is always less than the value of the node, and the right child is always greater than the value of the node.

This means that if we want to search for a particular value in the tree, we can start at the root node and then decide which direction to go based on whether the value we are looking for is less than or greater than the value of the current node. If the value is less than the current node, we go left; if it is greater, we go right. This process is repeated until we either find the value we are looking for or reach a null node, indicating that the value is not present in the tree.

One of the main benefits of using a BST is that it allows us to search for values in O(log n) time, where n is the number of nodes in the tree. This is much faster than searching through an unsorted list, which takes O(n) time.

Inserting and deleting elements from a BST is also relatively efficient. To insert a new element into the tree, we follow the same process as searching for a value, except that instead of stopping when we find the value we are looking for, we continue until we reach a null node. Then, we insert the new element as the left or right child of the node, depending on whether it is less than or greater than the value of the node. To delete a node from the tree, we need to find the node and then remove it while maintaining the BST property.

Here is an example implementation of a binary search tree class in JavaScript:

class TreeNode {
  constructor(value) {
    this.left = null
    this.right = null
    this.value = value
  }
}

class BinarySearchTree {
  constructor() {
    this.root = null
  }

  insert(value) {
    let newNode = new TreeNode(value)
    if (!this.root) {
      this.root = newNode
      return
    }
    let curr = this.root
    while (curr) {
      if (value <= curr.value) {
        if (curr.left) {
          curr = curr.left
        } else {
          curr.left = newNode
          break
        }
      } else {
        if (curr.right) {
          curr = curr.right
        } else {
          curr.right = newNode
          break
        }
      }
    }
  }
  lookup(value) {
    if (!this.root) return "Empty Array"
    let curr = this.root
    while (curr) {
      if (value === curr.value) {
        return curr
      } else if (value < curr.value) {
        curr = curr.left
      } else {
        curr = curr.right
      }
    }
    return "Not Found"
  }
  remove(value) {
    if (!this.root) return "Empty Array"
    let curr = this.root
    let parentNode = null
    while (curr) {
      if (value < curr.value) {
        parentNode = curr
        curr = curr.left
      } else if (value > curr.value) {
        parentNode = curr
        curr = curr.right
      } else {
        if (!curr.right) {
          if (!parentNode) {
            this.root = curr.left
          } else {
            if (curr.value < parentNode.value) {
              parentNode.left = curr.left
            } else if (curr.value > parentNode.value) {
              parentNode.right = curr.left
            }
          }
        } else if (!curr.right.left) {
          if (!parentNode) {
            this.root = curr.left
          } else {
            curr.right.left = curr.left
            if (curr.value < parentNode.value) {
              parentNode.left = curr.right
            } else if (curr.value > parentNode.value) {
              parentNode.right = curr.right
            }
          }
        } else {
          let leftmost = curr.right.left
          let leftmostParent = curr.right
          while (leftmost.left !== null) {
            leftmostParent = leftmost
            leftmost = leftmost.left
          }
          leftmostParent.left = leftmost.right
          leftmost.left = curr.left
          leftmost.right = curr.right
          if (!parentNode) {
            this.root = leftmost
          } else {
            if (curr.value < parentNode.value) {
              parentNode.left = leftmost
            } else {
              parentNode.right = leftmost
            }
          }
        }
      }
    }
  }
}

const tree = new BinarySearchTree()
tree.insert(9)
tree.insert(4)
tree.insert(6)
tree.insert(20)
tree.insert(170)
tree.insert(15)
tree.insert(1)
console.log(tree.lookup(20))
/**
TreeNode {
  left: TreeNode { left: null, right: null, value: 15 },
  right: TreeNode { left: null, right: null, value: 170 },
  value: 20
}
 */
console.log(tree.lookup(150)) // Not Found
console.log(JSON.stringify(traverse(tree.root)))
/**
 * {"value":9,
 *  "left":{"value":4,"left":{"value":1,"left":null,"right":null}, "right":{"value":6,"left":null,"right":null}},
 *  "right":{"value":20,"left":{"value":15,"left":null,"right":null},"right":{"value":170,"left":null,"right":null}}}
 */

//     9
//  4     20
//1  6  15  170

function traverse(node) {
  const tree = { value: node.value }
  tree.left = node.left === null ? null : traverse(node.left)
  tree.right = node.right === null ? null : traverse(node.right)
  return tree
}

const maxDepth = function (root) {
  if (!root) return 0
  let maxDepth = 0
  const helper = (root, depth) => {
    if (root.left) helper(root.left, depth + 1)
    if (root.right) helper(root.right, depth + 1)
    if (!root.left) {
      if (depth > maxDepth) maxDepth = depth
      return
    }
    if (!root.right) {
      if (depth > maxDepth) maxDepth = depth
      return
    }
  }
  helper(root, 1)
  return maxDepth
}

BSTs are commonly used in a variety of applications, such as

  • Implementing associative arrays
  • Representing mathematical expressions
  • Storing data in databases.
  • Algorithms for sorting and searching data, such as quicksort and merge sort.

Tree Traversal

Tree traversal is the process of visiting all the nodes in a tree in a specific order. There are three common types of tree traversal: inorder, preorder, and postorder.

Tree traversal is often used to perform some operation on all the nodes of a tree, such as printing the values of the nodes or evaluating a mathematical expression represented by the tree. It can also be used to create a copy of a tree or to delete all the nodes of a tree.

In order to implement tree traversal, we can use either a recursive or an iterative approach.

Let’s consider following BST as an example Binary Search Tree Example

Inorder traversal:

In an inorder traversal, we first visit the left child of a node, then the node itself, and then the right child. This means that the nodes of the tree are visited in the order: left child, root, right child.

function inorderTraversal(node) {
  if (node !== null) {
    inorderTraversal(node.left)
    console.log(node.data)
    inorderTraversal(node.right)
  }
}

For Above BST Inorder traversal array would be : [9,11,12,18,20,21,22]

Preorder traversal:

In a preorder traversal, we first visit the root node, then the left child, and then the right child. This means that the nodes of the tree are visited in the order: root, left child, right child.

function preorderTraversal(node) {
  if (node !== null) {
    console.log(node.data)
    preorderTraversal(node.left)
    preorderTraversal(node.right)
  }
}

For Above BST preorder traversal array would be : [18,11,9,12,21,20,22]

Postorder traversal:

In a postorder traversal, we first visit the left child, then the right child, and then the root node. This means that the nodes of the tree are visited in the order: left child, right child, root.

function postorderTraversal(node) {
  if (node !== null) {
    postorderTraversal(node.left)
    postorderTraversal(node.right)
    console.log(node.data)
  }
}

For Above BST preorder traversal array would be : [9,12,11,20,22,21,18]

It’s also possible to implement these tree traversal functions using an iterative approach rather than a recursive one. The iterative approach involves using a stack to keep track of the nodes that need to be visited. Here is an example of an iterative inorder traversal function:

function inorderTraversalIterative(root) {
  const stack = []
  let current = root

  while (current !== null || stack.length > 0) {
    while (current !== null) {
      stack.push(current)
      current = current.left
    }
    current = stack.pop()
    console.log(current.data)
    current = current.right
  }
}

Similar iterative approaches can be used for preorder and postorder traversal as well.

Conclusion

In conclusion, binary search trees (BSTs) are a type of binary tree data structure that are used for searching and sorting data. They are called search trees because they can be efficiently used to search for the presence or absence of a particular element in the tree, and they are called binary trees because each node has at most two children. BSTs are characterized by the property that the left child of a node is always less than the value of the node, and the right child is always greater than the value of the node. This allows us to search for values in O(log n) time, where n is the number of nodes in the tree. BSTs are also relatively efficient for inserting and deleting elements, making them a useful data structure for storing and manipulating data in a hierarchical manner.


Vishal Sharma

Hey there! This is Vishal Sharma. I reside and work at Gurgaon, India. I am a Software Engineer and primarily works with JavaScript, ReactJS and NodeJS.
LinkedIn Link

Welcome to my Javascript tech blog! Here, you'll find the latest news and updates in the world of Javascript, as well as tutorials and resources to help you improve your coding skills. From learning the basics of Javascript to advanced concepts like object-oriented programming, I post something for developers of all levels. Whether you're just starting out or you're a seasoned pro, you'll find valuable insights and information on our blog. So stay up-to-date with the latest in Javascript technology by bookmarking this page and checking back often.
Thank you for visiting!