Satyam Tripathi

Splay Tree

Splay Tree in data structures is a type of binary search tree that uses a splaying operation on the tree so the most frequently used elements can come closer to the root. This increases the insertion, deletion, and search operations in the tree.

Scope of the Article

  • This article defines a splay tree, its properties, operations on a splay tree, and the implementation of a splay tree in C/C++/Java/Python.
  • You will see different types of rotations performed on the splay tree.
  • You will also see the advantages and disadvantages of the splay tree.

Introduction to Splay Tree

As we know, the worst-case time complexity of operations like search, delete, and insert on a binary search tree is O(n), while the average time complexity is O(logn). The value of the left subtree in a binary search tree is smaller than the value of the root node, and the value of the right subtree is greater than the value of the root node. The time complexity, in this case, would be O(logn), and if we tried to skew left or right, the time complexity would be O(logn). We can reduce skewness by using AVL and Red-Black trees and can reduce complexity to O(logn).

But, can we do better than this?

The answer is YES. But how?

Here comes the concept of “Splay Tree”.

Note: If you are reading this article, then I will suggest that you should know about binary search trees. At least you know the basics of binary search trees, like how to search, delete, and insert operations are performed.

What is Splay Tree?

Have you ever thought that AVL and Red-Black trees are also self-adjusted trees? Then, what makes the Splay Tree different from the AVL and Red-Black trees? Yes, there is one operation called splaying, which makes it different from both AVL and the Red-Black Tree.

A splay tree contains all the operations of a binary search tree, like insertion, deletion, and searching. But it also contains one more operation, which is called splaying. In a splay tree, every operation is performed at the root of the tree. All operations in the splay tree involve one common operation called splaying.

You may have questioned what splaying is and why it differentiates splay trees from AVL and Red-Black trees. So, let me tell you about splaying. Splaying is the process of bringing an element to the root by performing suitable rotations.

By splaying elements in the tree, we can bring more frequently used elements closer to the root of the tree so that any operations like insertion, searching, and deletion can be performed quickly. This means, that after applying the splaying operation, more frequently used elements come closer to the root.

Suppose we have been given a binary search tree with different nodes and we know that in the binary search tree, elements to the left are smaller and those to the right are greater than the root node.

Splay Tree

For searching, we will perform the binary search method. Let’s say we want to search for element 9. As 9 is less than 11, we will come to the left of the root node. After performing a search operation, we need to do one thing called splaying. This means, that after splaying, the element on which we are operating should come to the root. Elements would come to the root after performing some rearrangements of elements or rotations in the tree.

Rotations in Splay Tree

To rearrange the tree, we need to perform some rotations. The rotations given below are the rotations that we are going to perform in the splay tree.

  • Zig rotation [Right Rotation]
  • Zig zig [Two Right Rotations]
  • Zag rotation [Left Rotation]
  • Zag zag [Two Left Rotations]
  • Zig zag [Zig followed by Zag]
  • Zag zig [Zag followed by Zig]

Zig rotation:

This rotation is similar to the right rotation in the AVL tree. In zig rotation, every node moves one position to the right of its current position. We use Zig rotation when the item which is to be searched is either a root node or a left child of the root node.

Let’s take a scenario where the search item is the left child of the root node.

In the above example, we have to search for node 9 in the tree. To search for the given node in the binary search tree, we need to perform the following steps:

Zig rotation

Step 1: First, we compare 9 with the root node. As 9 is less than 11, it is a left child of the root node.

Step 2: We have already seen above that once the element is found, we will perform splaying. Here the right rotation is performed so that 9 becomes the root node of the tree. Have a look at the diagram below.

Zig rotation

In the above diagram, we can see that node 9 has become the root node of the tree, this shows that the searching is completed.

Zig zig rotation:

It’s a kind of double zig rotation. Here we perform zig rotation two times. Every node moves two positions to the right of its current position. But why are we doing this?

We are doing this because sometimes situations arise where we need to search for the item that has both parent and grandparent. In such cases, we have to perform four rotations for splaying.

In the example given below, suppose we have to search for element 3.

Zig zig rotation

Step 1: First, we have to perform a search operation in the tree as we did previously, which means a BST operation. As 3 is less than 6 and 4, it will be at the left of node 4. So we can say that element 3 has a parent of 4 and a grandparent of 6.

Step 2: We have to perform splaying, which means we have to make node 3 the root node. So here we will perform two right rotations because the elements to be searched have both parent and grandparent.

Zig zig rotation

In the above diagram, we can see that node 3 has become the root node of the tree, this shows that the searching is completed.

Zag rotation:

This rotation is similar to the left rotation in the AVL tree. In zag rotation, every node moves one position to the left of its current position. We use Zag rotation when the item which is to be searched is either a root node or a right child of the root node.

Let’s see the case where the element to be searched for is present on the right of the root node of the tree. Now let’s say we have to search for 13, which is present on the right of the root node of the tree.

Zag rotation

The steps involved in searching are given below:

Step 1: First, we compare 13 with the root node. As 13 is greater than the root node of the tree, it is the right child of the root node.

Step 2: Once the element is found, we will perform splaying as we did in the previous examples. Here we will perform a left rotation so that 13 becomes the root node of the tree.

Zag rotation

In the above diagram, we can see that node 13 has become the root node of the tree, this shows that the searching is completed.

Zag Zag Rotation:

It’s a kind of double zag rotation. Here we perform zag rotation two times. Every node moves two positions to the left of its current position. But why are we doing this?

We are doing this because sometimes situations arise where we need to search for the item that has both parent and grandparent. In such cases, we have to perform four rotations for splaying.

Now we will try to understand this case with some examples:

In the example given below, suppose we have to search for element 7.

Zag zag rotation

Step 1: First, we have to perform a search operation in the tree as we did previously, which means a BST operation. As 7 is greater than 4 and 6, it will be at the right of node 6. So we can say that element 7 has a parent of 6 and a grandparent of 4.

Step 2: We have to perform splaying, which means we have to make node 7 the root node. So here we will perform two left rotations because the elements to be searched have both parent and grandparent.

Zag zag rotation

In the above diagram, we can see that node 7 has become the root node of the tree, this shows that the searching is completed.

Zig Zag rotation

This type of rotation is a sequence of zig rotations followed by zag rotations. So far, we’ve seen that both the parent and the grandparent are in a RR or LL relationship. Now, here we will see the RL and LR kinds of relationships between parent and grandparent. Every node moves one position to the right, followed by one position to the left of its current position.

Zig zag rotation

Suppose we want to search for element 5. Here first we perform a BST searching operation. Like 5, it is greater than the root node 4 and smaller than the node 6. So 5 would be the left child of node 6.

So an RL relationship exists since node 5 is to the left of node 6 and node 6 is to the right of node 4. So first we will perform the right rotation on node 6, and then node 6 will move downwards, and node 5 will come upwards, as you can see in the example given below. After that, we will perform zag rotation(left rotation) at 4, and we will see that 5 will become the root node of the tree.

Zig zag rotation

As we can observe in the above tree, node 5 has become the root node; therefore, the search is completed. In this case, first, we have performed the zig rotation and then the zag rotation. So it is known as zig-zag rotation.

Zag Zig Rotation

This rotation is similar to the Zig-zag rotation, the only difference is that here every node moves one position to the left, followed by one position to the right of its current position.

Zag zig rotation

Suppose we want to search for element 5. Here first we perform a BST searching operation. Like 5, it is smaller than the root node 6 and greater than node 4. So 5 would be the right child of node 4, and 4 would be the left child of the root node 6.

So here $LR$ relationship exists since node 5 is to the right of node 4 and node 4 is to the left of node 6. So first we will perform the left rotation on node 4, and then node 4 will move downwards, and node 5 will come upwards, as you can see in the example given below. And after that, we will perform one zig rotation (right rotation) at 6, so finally, we will get 5 as the root node of the tree.

Zag zig rotation

As we can observe in the above tree, node 5 has become the root node; therefore, the searching is completed. In this case, first, we have performed the zag rotation and then the zig rotation. So it is known as zag-zig rotation.

Advantages of Splay Tree

  • In the AVL and Red-Black trees, we need to store some information. Like in AVL trees, we need to store the balance factor of each node, and in the red-black trees, we also need to store one extra bit of information that denotes the color of the node, either black or red.
  • Splay tree is the fastest type of binary search tree, which is used in a variety of practical applications such as GCC compilers.
  • Improve searching by moving frequently accessed nodes closer to the root node. One of the practical uses is cache implementation, in which recently used data is saved in the cache so that we can access the data more quickly without going into memory.

Disadvantages of Splay Tree

The main disadvantage of the splay tree is that trees are not strictly balanced, but rather roughly balanced. When the splay trees are linear, the time complexity is O(n).

Implementation of Splay Tree

C

#include <stdio.h>
#include <stdlib.h>

// Tree Node
struct node
{
    int key;
    struct node *left, *right;
};

// Allocates a new node with the given key and NULL left and right pointers.
struct node *TreeNode(int key)
{
    struct node *node = (struct node *)malloc(sizeof(struct node));
    node->key = key;
    node->left = node->right = NULL;
    return (node);
}

// A utility function for right-rotating a y-rooted subtree.
struct node *rightRotate(struct node *x)
{
    struct node *y = x->left;
    x->left = y->right;
    y->right = x;
    return y;
}

// A utility function for right-rotating a x-rooted subtree.
struct node *leftRotate(struct node *x)
{
    struct node *y = x->right;
    x->right = y->left;
    y->left = x;
    return y;
}

// If the key is present in the tree, this function moves it to the root.
// If the key is not present, it returns the last item accessed by root.
// This function modifies the tree and returns the modified root.
struct node *splay(struct node *root, int key)
{
    // Root is NULL or key is present at root.
    if (root == NULL || root->key == key)
        return root;

    // Key lies in left subtree!
    if (root->key > key)
    {
        if (root->left == NULL)
            return root;

        // Zig-Zig (Left Left)
        if (root->left->key > key)
        {
            // First, recursively bring the key as the root of left-left.
            root->left->left = splay(root->left->left, key);

            // Do the first rotation for the root, followed by the second rotation.
            root = rightRotate(root);
        }
        else if (root->left->key < key) // Zig-Zag (Left Right)
        {
            //  First, recursively bring the key as the root of left-right.
            root->left->right = splay(root->left->right, key);

            // Do first rotation for root->left
            if (root->left->right != NULL)
                root->left = leftRotate(root->left);
        }

        // Do second rotation for root
        return (root->left == NULL) ? root : rightRotate(root);
    }
    // Key lies in right subtree
    else
    {
        if (root->right == NULL)
            return root;

        // Zag-Zig (Right Left)
        if (root->right->key > key)
        {
            // Bring the key as root of right-left
            root->right->left = splay(root->right->left, key);

            // Do first rotation for root->right
            if (root->right->left != NULL)
                root->right = rightRotate(root->right);
        }
        // Zag-Zag (Right Right)
        else if (root->right->key < key)
        {
            // Bring the key as root of right-right and do first rotation
            root->right->right = splay(root->right->right, key);
            root = leftRotate(root);
        }

        // Do second rotation for root
        return (root->right == NULL) ? root : leftRotate(root);
    }
}

// The search function for Splay Tree. Note that this function returns the new root of the splay-tree.
// If a key is present in the tree, then it is moved to the root.
struct node *bstSearch(struct node *root, int key)
{
    return splay(root, key);
}

// A utility function to print preorder traversal of the tree.
void preOrder(struct node *root)
{
    if (root != NULL)
    {
        printf("%d ", root->key);
        preOrder(root->left);
        preOrder(root->right);
    }
}

// main function
int main()
{
    struct node *root = TreeNode(100);
    root->left = TreeNode(50);
    root->right = TreeNode(200);
    root->left->left = TreeNode(40);
    root->left->left->left = TreeNode(30);
    root->left->left->left->left = TreeNode(20);

    root = bstSearch(root, 20);
    preOrder(root);
    return 0;
}

C++

#include <bits/stdc++.h>
using namespace std;

// Tree Node
class node
{
public:
    int key;
    node *left, *right;
};

// Allocate a new node with the given key and NULL left and right pointers.
node *TreeNode(int key)
{
    node *Node = new node();
    Node->key = key;
    Node->left = Node->right = NULL;
    return (Node);
}

// A utility function for right-rotating a y-rooted subtree.
node *rightRotate(node *x)
{
    node *y = x->left;
    x->left = y->right;
    y->right = x;
    return y;
}

// A utility function for right-rotating a x-rooted subtree.
node *leftRotate(node *x)
{
    node *y = x->right;
    x->right = y->left;
    y->left = x;
    return y;
}

// If the key is present in the tree, this function moves it to the root.
// If the key is not present, it returns the last item accessed by root.
// This function modifies the tree and returns the modified root.
node *splay(node *root, int key)
{
    // Root is NULL or key is present at root.
    if (root == NULL || root->key == key)
        return root;

    // Key lies in left subtree!
    if (root->key > key)
    {
        // Key is not in tree, we are done
        if (root->left == NULL)
            return root;

        // Zig-Zig (Left Left)
        if (root->left->key > key)
        {
            // First, recursively bring the key as the root of left-left.
            root->left->left = splay(root->left->left, key);

            // Do the first rotation for the root, followed by the second rotation.
            root = rightRotate(root);
        }
        else if (root->left->key < key) // Zig-Zag (Left Right)
        {
            //  First, recursively bring the key as the root of left-right.
            root->left->right = splay(root->left->right, key);

            // Do first rotation for root->left
            if (root->left->right != NULL)
                root->left = leftRotate(root->left);
        }

        // Do second rotation for root
        return (root->left == NULL) ? root : rightRotate(root);
    }
    // Key lies in right subtree
    else
    {
        if (root->right == NULL)
            return root;

        // Zag-Zig (Right Left)
        if (root->right->key > key)
        {
            // Bring the key as root of right-left
            root->right->left = splay(root->right->left, key);

            // Do first rotation for root->right
            if (root->right->left != NULL)
                root->right = rightRotate(root->right);
        }
        // Zag-Zag (Right Right)
        else if (root->right->key < key)
        {
            // Bring the key as root of right-right and do first rotation
            root->right->right = splay(root->right->right, key);
            root = leftRotate(root);
        }

        // Do second rotation for root
        return (root->right == NULL) ? root : leftRotate(root);
    }
}

// The search function for Splay Tree. Note that this function returns the new root of the splay-tree.
// If a key is present in the tree, then it is moved to the root.
node *bstSearch(node *root, int key)
{
    return splay(root, key);
}

// A utility function to print preorder traversal of the tree.
void preOrder(node *root)
{
    if (root != NULL)
    {
        cout << root->key << " ";
        preOrder(root->left);
        preOrder(root->right);
    }
}

// Driver Code
int main()
{
    node *root = TreeNode(100);
    root->left = TreeNode(50);
    root->right = TreeNode(200);
    root->left->left = TreeNode(40);
    root->left->left->left = TreeNode(30);
    root->left->left->left->left = TreeNode(20);

    root = bstSearch(root, 20);
    preOrder(root);
    return 0;
}

Java

class Main{
    // Tree Node
    static class node
    {

        int key;
        node left, right;
    };

    // Allocates a new node with the given key and NULL left and right pointers.
    static node TreeNode(int key)
    {
        node Node = new node();
        Node.key = key;
        Node.left = Node.right = null;
        return (Node);
    }

    // A utility function for right-rotating a y-rooted subtree.
    static node rightRotate(node x)
    {
        node y = x.left;
        x.left = y.right;
        y.right = x;
        return y;
    }

    // A utility function for right-rotating a x-rooted subtree.
    static node leftRotate(node x)
    {
        node y = x.right;
        x.right = y.left;
        y.left = x;
        return y;
    }

    // If the key is present in the tree, this function moves it to the root.
    // If the key is not present, it returns the last item accessed by root.
    // This function modifies the tree and returns the modified root.
    static node splay(node root, int key)
    {
        // Root is NULL or key is present at root.
        if (root == null || root.key == key)
            return root;

        // Key lies in left subtree!
        if (root.key > key)
        {
            if (root.left == null) return root;

            // Zig-Zig (Left Left)
            if (root.left.key > key)
            {
                // First, recursively bring the key as the root of left-left.
                root.left.left = splay(root.left.left, key);

                // Do the first rotation for the root, followed by the second rotation.
                root = rightRotate(root);
            }
            // Zig-Zag (Left Right)
            else if (root.left.key < key) 
            {
                //  First, recursively bring the key as the root of left-right.
                root.left.right = splay(root.left.right, key);

                // Do first rotation for root->left
                if (root.left.right != null)
                    root.left = leftRotate(root.left);
            }

            // Do second rotation for root
            return (root.left == null) ? root : rightRotate(root);
        }
        // Key lies in right subtree
        else 
        {
            if (root.right == null) return root;

            // Zag-Zig (Right Left)
            if (root.right.key > key)
            {
                // Bring the key as root of right-left
                root.right.left = splay(root.right.left, key);

                // Do first rotation for root->right
                if (root.right.left != null)
                    root.right = rightRotate(root.right);
            }
            // Zag-Zag (Right Right)
            else if (root.right.key < key)
            {
                // Bring the key as root of right-right and do first rotation
                root.right.right = splay(root.right.right, key);
                root = leftRotate(root);
            }

            // Do second rotation for root
            return (root.right == null) ? root : leftRotate(root);
        }
    }

    // The search function for Splay Tree. Note that this function returns the new root of the splay-tree.
    // If a key is present in the tree, then it is moved to the root.
    static node bstSearch(node root, int key)
    {
        return splay(root, key);
    }

    // A utility function to print preorder traversal of the tree.
    static void preOrder(node root)
    {
        if (root != null)
        {
            System.out.print(root.key + " ");
            preOrder(root.left);
            preOrder(root.right);
        }
    }

    // Driver code
    public static void main(String[] args)
    {
        node root = TreeNode(100);
        root.left = TreeNode(50);
        root.right = TreeNode(200);
        root.left.left = TreeNode(40);
        root.left.left.left = TreeNode(30);
        root.left.left.left.left = TreeNode(20);

        root = bstSearch(root, 20);
        preOrder(root);
    }
}

Python

class TreeNode:
    def __init__(self, data):
        self.data = data
        self.parent = None
        self.left = None
        self.right = None


class SplayTree:
    def __init__(self):
        self.root = None

    def leftRotate(self, x):
        y = x.right
        x.right = y.left
        if y.left != None:
            y.left.parent = x

        y.parent = x.parent
        # x is root
        if x.parent == None:
            self.root = y
        # x is left child
        elif x == x.parent.left:
            x.parent.left = y
        # x is right child
        else:
            x.parent.right = y

        y.left = x
        x.parent = y

    def rightRotate(self, x):
        y = x.left
        x.left = y.right
        if y.right != None:
            y.right.parent = x

        y.parent = x.parent
        # x is root
        if x.parent == None:
            self.root = y
        # x is right child
        elif x == x.parent.right:
            x.parent.right = y
        # x is left child
        else:
            x.parent.left = y

        y.right = x
        x.parent = y

    def splay(self, n):
        # node is not root
        while n.parent != None:
            # node is child of root, one rotation
            if n.parent == self.root:
                if n == n.parent.left:
                    self.rightRotate(n.parent)
                else:
                    self.leftRotate(n.parent)

            else:
                p = n.parent
                g = p.parent  # grandparent

                if n.parent.left == n and p.parent.left == p:  # both are left children
                    self.rightRotate(g)
                    self.rightRotate(p)

                elif n.parent.right == n and p.parent.right == p:  # both are right children
                    self.leftRotate(g)
                    self.leftRotate(p)

                elif n.parent.right == n and p.parent.left == p:
                    self.leftRotate(p)
                    self.rightRotate(g)

                elif n.parent.left == n and p.parent.right == p:
                    self.rightRotate(p)
                    self.leftRotate(g)

    def insert(self, n):
        y = None
        temp = self.root
        while temp != None:
            y = temp
            if n.data < temp.data:
                temp = temp.left
            else:
                temp = temp.right

        n.parent = y

        if y == None:  # newly added node is root
            self.root = n
        elif n.data < y.data:
            y.left = n
        else:
            y.right = n

        self.splay(n)

    def bstSearch(self, n, x):
        if x == n.data:
            self.splay(n)
            return n
        elif x < n.data:
            return self.bstSearch(n.left, x)
        elif x > n.data:
            return self.bstSearch(n.right, x)
        else:
            return None

    def inOrder(self, n):
        if n != None:
            self.inOrder(n.left)
            print(n.data, end=' ')
            self.inOrder(n.right)


if __name__ == '__main__':
    tree = SplayTree()
    a = TreeNode(10)
    b = TreeNode(20)
    c = TreeNode(30)
    d = TreeNode(100)
    e = TreeNode(90)
    f = TreeNode(40)
    g = TreeNode(50)
    tree.insert(a)
    tree.insert(b)
    tree.insert(c)
    tree.insert(d)
    tree.insert(e)
    tree.insert(f)
    tree.insert(g)
    tree.bstSearch(tree.root, 90)
    tree.inOrder(tree.root)
    print()

Ignite Data Structures Brilliance! Enroll in Scaler Academy’s Online DSA Course to Hone Your Coding Skills.

Conclusion

  • Splay Tree in data structures is a type of binary search tree that uses a splaying operation on the tree so the most frequently used elements can come closer to the root.
  • Splay tree is the fastest type of binary search tree, which is used in a variety of practical applications such as GCC compilers.

See also

Binary Search Tree (BSTs)

Author