Treap data structure in Javascript

Treap data structure is a Cartesian tree in which each key is given a (randomly chosen) numeric priority described by Raimund Seidel and Cecilia R. Aragon in 1989.

In this tutorial, we will learn about the Treap data structure and how to implement a Treap in javascript.

What is Treap data structure?.

A Treap data structure is a Balanced Binary Search Tree, which combines the best of both the heaps and binary search trees, but not guaranteed to have height as O(Log n).

It store two values.
1. A key (Ordered according to the BST property).
2. And a randomly chosen numeric priority (A random value ordered according to max-heap property).

The structure of the tree is determined by the requirement that it be heap-ordered: that is, the priority number for any non-leaf node must be greater than or equal to the priority of its children.

It is often considered as a good platform to build the randomized binary search tree.

Treap data structure

List of operations performed on treap.

  • insertNode(key):- Adds a new node with random priority.
  • deleteNode(key):- Removes a node with the given key.
  • searchNode(key):- Returns true if key is present in the treap, false otherwise.
  • printTreap:- Prints the node in the BST order.

Implementing treap data structure in Javascript.

We will need to helper functions in-order to implement a treap rotateLeft, rotateRight which reverses the parent-child relation.

Base structure

//Generates a random number
const getRandom = (max, min = 1) => {
  return Math.floor((Math.random() * (max - min + 1)) + min);
}

const Treap = function() {
  //Node with priority
  const Node = function(data){
    this.data = data;
    this.priority = getRandom(100);
    this.left = this.right = null;
  }
  
  //To store the root
  let root = null;
  
  const rotateLeft = function(root) {
    const R = root.right;
    const X = root.right.left;

    // rotate
    R.left = root;
    root.right = X;

    // set new root
    return R;
  }
  
  const rotateRight = function(root) {
    const L = root.left;
    const Y = root.left.right;

    // rotate
    L.right = root;
    root.left = Y;

    // set new root
    return L;
  }

  //Other methods will go here
}

Insert operation on the treap.

To insert a key in the treap, create a new node with random priority and add it at the leaf position using the BST property to determine its initial position and then if the new node is not the root and has greater priority value than the parent then perform the tree rotation, to reverse its position with the parent.

Insert in treap

// Recursive function to insert a given key with a priority into Treap
  this.insertNode = function(data) {
    
    const insertNodeHelper = (root, data) => {
      // base case
        if (root === null) {
            return new Node(data);
        }
 
        // if data is less than the root node, insert in the left subtree
        // else insert in the right subtree
        if (data < root.data) {
            root.left = insertNodeHelper(root.left, data);
 
            // rotate right if heap property is violated
            if (root.left != null && root.left.priority > root.priority) {
                root = rotateRight(root);
            }
        }
        else {
            root.right = insertNodeHelper(root.right, data);
 
            // rotate left if heap property is violated
            if (root.right != null && root.right.priority > root.priority) {
                root = rotateLeft(root);
            }
        }
 
        return root;
    } 
    
    root = insertNodeHelper(root, data);
 }

Delete operation on treap.

To remove a node with given key from the treap we have to check for the following conditions.

  • If the node is the leaf then remove it.
  • If the node has only single child then remove the node and replace its position with its child.
  • In case node has two child, swap its position in the tree with its immediate successor in the sorted order, resulting in one of the previous cases. Swapping may disrupt the heap-order priority, so we need to perform additional rotations to restore the property.

Delete node from treap

// Recursive function to delete a key from the given Treap
 this.deleteNode = function(key) {
     const deleteNodeHelper = (root, key) => {
        // base case: key not found in tree
        if (root === null) {
            return null;
        }
 
        // if key is less than the root node, recur for left subtree
        if (key < root.data) {
            root.left = deleteNodeHelper(root.left, key);
        }
 
        // if key is more than the root node, recur for right subtree
        else if (key > root.data) {
            root.right = deleteNodeHelper(root.right, key);
        }
 
        // if key found
        else
        {
            // Case 1: node to be deleted has no children (it is a leaf node)
            if (root.left == null && root.right == null)
            {
                // deallocate the memory and update root to null
                root = null;
            }
 
            // Case 2: node to be deleted has two children
            else if (root.left != null && root.right != null)
            {
                // if left child has less priority than right child
                if (root.left.priority < root.right.priority)
                {
                    // call rotateLeft on root
                    root = rotateLeft(root);
 
                    // recursively delete the left child
                    root.left = deleteNodeHelper(root.left, key);
                }
                else
                {
                    // call rotateRight on root
                    root = rotateRight(root);
 
                    // recursively delete the right child
                    root.right = deleteNodeHelper(root.right, key);
                }
            }
 
            // Case 3: node to be deleted has only one child
            else
            {
                // find child node
                const child = (root.left != null) ? root.left: root.right;
                root = child;
            }
        }
 
        return root;
     }
     
     root = deleteNodeHelper(root, key);
 }

Search operation in treap

To search a key, we can use the standard search operation of BST, ignoring the priority.

// Recursive function to search for a key in the given Treap
  this.searchNode = function(key) {
     const searchNodeHelper = (root, key) => {
        // if key is not present in the key
        if (root === null) {
            return false;
        }
 
        // if key is found
        if (root.data == key) {
            return true;
        }
 
        // if key is less than the root node, search in the left subtree
        if (key < root.data) {
            return searchNodeHelper(root.left, key);
        }
 
        // else search in the right subtree
        return searchNodeHelper(root.right, key);
     }
     
     return searchNodeHelper(root, key);
}

Print operation on treap.

Print the nodes in the BST order.

 this.printTreap = function() {
   const printTreapHealper = (root) => {
     // Base case
     if (root === null) {
       return;
     }

     // print right child first
     printTreapHealper(root.right);

     console.log(root.data + "(" + root.priority + ")");

     // print left child
     printTreapHealper(root.left);
   }

   printTreapHealper(root);
 }
}

Complete code of Treap data structure in Javascript.

const getRandom = (max, min = 1) => {
  return Math.floor((Math.random() * (max - min + 1)) + min);
}

const Treap = function() {
  const Node = function(data){
    this.data = data;
    this.priority = getRandom(100);
    this.left = this.right = null;
  }
  
  let root = null;
  
  const rotateLeft = function(root) {
    const R = root.right;
    const X = root.right.left;

    // rotate
    R.left = root;
    root.right = X;

    // set new root
    return R;
  }
  
  const rotateRight = function(root) {
    const L = root.left;
    const Y = root.left.right;

    // rotate
    L.right = root;
    root.left = Y;

    // set new root
    return L;
  }
  
  // Recursive function to insert a given key with a priority into Treap
  this.insertNode = function(data) {
    
    const insertNodeHelper = (root, data) => {
      // base case
        if (root === null) {
            return new Node(data);
        }
 
        // if data is less than the root node, insert in the left subtree
        // else insert in the right subtree
        if (data < root.data) {
            root.left = insertNodeHelper(root.left, data);
 
            // rotate right if heap property is violated
            if (root.left != null && root.left.priority > root.priority) {
                root = rotateRight(root);
            }
        }
        else {
            root.right = insertNodeHelper(root.right, data);
 
            // rotate left if heap property is violated
            if (root.right != null && root.right.priority > root.priority) {
                root = rotateLeft(root);
            }
        }
 
        return root;
    } 
    
    root = insertNodeHelper(root, data);
 }
    
// Recursive function to search for a key in the given Treap
  this.searchNode = function(key) {
     const searchNodeHelper = (root, key) => {
        // if key is not present in the key
        if (root === null) {
            return false;
        }
 
        // if key is found
        if (root.data == key) {
            return true;
        }
 
        // if key is less than the root node, search in the left subtree
        if (key < root.data) {
            return searchNodeHelper(root.left, key);
        }
 
        // else search in the right subtree
        return searchNodeHelper(root.right, key);
     }
     
     return searchNodeHelper(root, key);
}
  
 // Recursive function to delete a key from the given Treap
 this.deleteNode = function(key) {
     const deleteNodeHelper = (root, key) => {
        // base case: key not found in tree
        if (root === null) {
            return null;
        }
 
        // if key is less than the root node, recur for left subtree
        if (key < root.data) {
            root.left = deleteNodeHelper(root.left, key);
        }
 
        // if key is more than the root node, recur for right subtree
        else if (key > root.data) {
            root.right = deleteNodeHelper(root.right, key);
        }
 
        // if key found
        else
        {
            // Case 1: node to be deleted has no children (it is a leaf node)
            if (root.left == null && root.right == null)
            {
                // deallocate the memory and update root to null
                root = null;
            }
 
            // Case 2: node to be deleted has two children
            else if (root.left != null && root.right != null)
            {
                // if left child has less priority than right child
                if (root.left.priority < root.right.priority)
                {
                    // call rotateLeft on root
                    root = rotateLeft(root);
 
                    // recursively delete the left child
                    root.left = deleteNodeHelper(root.left, key);
                }
                else
                {
                    // call rotateRight on root
                    root = rotateRight(root);
 
                    // recursively delete the right child
                    root.right = deleteNodeHelper(root.right, key);
                }
            }
 
            // Case 3: node to be deleted has only one child
            else
            {
                // find child node
                const child = (root.left != null) ? root.left: root.right;
                root = child;
            }
        }
 
        return root;
     }
     
     root = deleteNodeHelper(root, key);
 }
   
 // Utility function to print two-dimensional view of a Treap using
 // reverse inorder traversal
 this.printTreap = function() {
   const printTreapHealper = (root) => {
     // Base case
     if (root === null) {
       return;
     }

     // print right child first
     printTreapHealper(root.right);

     console.log(root.data + "(" + root.priority + ")");

     // print left child
     printTreapHealper(root.left);
   }

   printTreapHealper(root);
 }
}
Input:
const arr = [5, 2, 1, 4, 9, 8, 10];
const treap = new Treap();

for(let val of arr){
  root = treap.insertNode(val);
}

treap.printTreap();
root = treap.deleteNode(1);
console.log(" ");
console.log(treap.searchNode(8));
console.log(" ");
treap.printTreap();

Output:
"10(9)"
"9(71)"
"8(5)"
"5(21)"
"4(62)"
"2(8)"
"1(25)"
" "
true
" "
"10(9)"
"9(71)"
"8(5)"
"5(21)"
"4(62)"
"2(8)"

Time complexity of treap data structure

# Access Search Insert Delete
Average ΘLog(N) ΘLog(N) ΘLog(N) ΘLog(N)
Worst OLog(N) OLog(N) OLog(N) OLog(N)

Space complexity

# Space
Worst N