Binary Search Trees (BST) in TypeScript

Hello again! This lesson's topic is Binary Search Trees (BST) in TypeScript. A BST is a data structure that stores key-value pairs in an ordered manner, making data manipulation organized and efficient. TypeScript doesn't include a built-in binary search tree data structure. However, we can utilize the @datastructures-js/binary-search-tree library to manage our data in a sorted and efficient manner. Today's goal is to work with BSTs using TypeScript.

What is a Binary Search Tree

A Binary Search Tree (BST) is a node-based data structure where each node has at most two children referred to as the left child and the right child. The key in each node must be greater than (or equal to) any key stored in the left subtree, and less than (or equal to) any key stored in the right subtree. This property makes the binary search tree an efficient data structure for operations such as insertion, deletion, and searching.

Example of a Binary Search Tree

Consider the following BST:

In this tree:

  • The root node is 50.
  • The left subtree of 50 contains nodes 30, 20, and 40.
  • The right subtree of 50 contains nodes 70, 60, and 80.
  • The key in the root node (50) is greater than all keys in its left subtree (20, 30, 40) and less than all keys in its right subtree (60, 70, 80).
  • And this property is satisfied for all nodes, not just the root.

Due to its sorted nature, a BST enables binary search on the data, leading to the following time complexities for operations:

  • Search: O(logn)O(log n) on average, as each comparison allows us to ignore half of the remaining tree. This efficiency depends on the tree's balance; if the tree becomes unbalanced (e.g., resembling a linked list), the time complexity could degrade to O(n)O(n).
  • Insertion: O(logn)O(log n) on average, since each step through the tree involves comparing values and deciding to move left or right. In a balanced BST, this makes insertions efficient.
  • Deletion: O(logn)O(log n) on average. Deletion is more complex than insertion because it depends on the node’s location and the number of children it has. For example:
    • If the node has no children, we can remove it directly.
    • If it has one child, we bypass the node by linking its parent directly to the child.
    • If it has two children, we need to replace it with its in-order successor (the smallest node in the right subtree) or in-order predecessor (the largest node in the left subtree).

Now, let's move forward and see how to implement and work with BSTs in TypeScript using the @datastructures-js/binary-search-tree library.

Traversing BST Methods

Using the BinarySearchTree, we can implement several useful methods for managing and traversing the BST:

  • lowerBound(searchKey: T): This method finds the node with the biggest key less than or equal to a given searchKey.
  • upperBound(searchKey: T): This method finds the node with the smallest key greater than or equal to a given searchKey.
  • remove(searchKey: T): Removes the specified key and its associated value.
  • find(searchKey: T): Returns the node for the key if it exists.
  • min(): Returns the node with the smallest key.
  • max(): Returns the node with the largest key.
Using Binary Search Trees

We will use the BinarySearchTree class from the @datastructures-js/binary-search-tree package to handle sorted elements. Here's how to get started:

Storing Key-Value Pairs

Consider the following TypeScript code, which demonstrates storing key-value pairs using arrays. To manage these pairs, we will initialize the BinarySearchTree with a custom comparison function:

In this example, we pass a custom comparison function compareArrays to the BinarySearchTree constructor. This function takes two arguments, a and b, which are arrays representing key-value pairs. The function compares the first elements of these arrays (the keys) using localeCompare to ensure the tree is sorted based on the keys in lexicographical order.

In the lowerBound and upperBound methods, the second parameter is a boolean that dictates whether the search is inclusive or exclusive:

  • By default, if the second parameter is not provided or set to true, the search is inclusive, meaning it will return the node with the search key if it exists.
  • If the second parameter is false, the search is exclusive, meaning it will find the next closest node and exclude the node with the exact search key. This is useful for scenarios where you need to find the bounds around a given key.
Lesson Summary

Congratulations! You have successfully delved into Binary Search Trees in TypeScript. This exploration included understanding how to utilize the @datastructures-js/binary-search-tree library to manage sorted data functionality, creating instances with single elements and key-value pairs, and navigating the valuable methods associated with this data structure. Keep practicing to fortify your understanding and expand your skill set!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal