Open In App

Introduction to Splay tree data structure

Splay tree is a self-adjusting binary search tree data structure, which means that the tree structure is adjusted dynamically based on the accessed or inserted elements. In other words, the tree automatically reorganizes itself so that frequently accessed or inserted elements become closer to the root node.

  1. The splay tree was first introduced by Daniel Dominic Sleator and Robert Endre Tarjan in 1985. It has a simple and efficient implementation that allows it to perform search, insertion, and deletion operations in O(log n) amortized time complexity, where n is the number of elements in the tree.
  2. The basic idea behind splay trees is to bring the most recently accessed or inserted element to the root of the tree by performing a sequence of tree rotations, called splaying. Splaying is a process of restructuring the tree by making the most recently accessed or inserted element the new root and gradually moving the remaining nodes closer to the root.
  3. Splay trees are highly efficient in practice due to their self-adjusting nature, which reduces the overall access time for frequently accessed elements. This makes them a good choice for applications that require fast and dynamic data structures, such as caching systems, data compression, and network routing algorithms.
  4. However, the main disadvantage of splay trees is that they do not guarantee a balanced tree structure, which may lead to performance degradation in worst-case scenarios. Also, splay trees are not suitable for applications that require guaranteed worst-case performance, such as real-time systems or safety-critical systems.

Overall, splay trees are a powerful and versatile data structure that offers fast and efficient access to frequently accessed or inserted elements. They are widely used in various applications and provide an excellent tradeoff between performance and simplicity.

A splay tree is a self-balancing binary search tree, designed for efficient access to data elements based on their key values.

Operations in a splay tree:

Here's a step-by-step explanation of the rotation operations:

Rotations in Splay Tree

1) Zig Rotation:

The Zig Rotation in splay trees operates in a manner similar to the single right rotation in AVL Tree rotations. This rotation results in nodes moving one position to the right from their current location. For example, consider the following scenario:

Zig Rotation (Single Rotation)

2) Zag Rotation:

The Zag Rotation in splay trees operates in a similar fashion to the single left rotation in AVL Tree rotations. During this rotation, nodes shift one position to the left from their current location. For instance, consider the following illustration:

Zag Rotation (Single left Rotation)

 3) Zig-Zig Rotation:

The Zig-Zig Rotation in splay trees is a double zig rotation. This rotation results in nodes shifting two positions to the right from their current location. Take a look at the following example for a better understanding:

Zig-Zig Rotation (Double Right Rotation)

4) Zag-Zag Rotation:

In splay trees, the Zag-Zag Rotation is a double zag rotation. This rotation causes nodes to move two positions to the left from their present position. For example:

Zag-Zag Rotation (Double left rotation)

5) Zig-Zag Rotation:

The Zig-Zag Rotation in splay trees is a combination of a zig rotation followed by a zag rotation. As a result of this rotation, nodes shift one position to the right and then one position to the left from their current location. The following illustration provides a visual representation of this concept:

Zig- Zag rotation


6) Zag-Zig Rotation:

The Zag-Zig Rotation in splay trees is a series of zag rotations followed by a zig rotation. This results in nodes moving one position to the left, followed by a shift one position to the right from their current location. The following illustration offers a visual representation of this concept:

Zag-Zig Rotation

Below is the C++ code to implement rotations in the Splay tree:

#include <iostream>

using namespace std;

struct Node {
    int key;
    Node *left, *right;
};

Node* newNode(int key) {
    Node* node = new Node();
    node->key = key;
    node->left = node->right = nullptr;
    return node;
}

Node* rightRotate(Node* x) {
    Node* y = x->left;
    x->left = y->right;
    y->right = x;
    return y;
}

Node* leftRotate(Node* x) {
    Node* y = x->right;
    x->right = y->left;
    y->left = x;
    return y;
}

Node* splay(Node* root, int key) {
    if (root == nullptr || root->key == key)
        return root;

    if (root->key > key) {
        if (root->left == nullptr)
            return root;
        if (root->left->key > key) {
            root->left->left = splay(root->left->left, key);
            root = rightRotate(root);
        }
        else if (root->left->key < key) {
            root->left->right = splay(root->left->right, key);
            if (root->left->right != nullptr)
                root->left = leftRotate(root->left);
        }
        return (root->left == nullptr) ? root : rightRotate(root);
    }
    else {
        if (root->right == nullptr)
            return root;
        if (root->right->key > key) {
            root->right->left = splay(root->right->left, key);
            if (root->right->left != nullptr)
                root->right = rightRotate(root->right);
        }
        else if (root->right->key < key) {
            root->right->right = splay(root->right->right, key);
            root = leftRotate(root);
        }
        return (root->right == nullptr) ? root : leftRotate(root);
    }
}

Node* insert(Node* root, int key) {
    if (root == nullptr)
        return newNode(key);

    root = splay(root, key);

    if (root->key == key)
        return root;

    Node* node = newNode(key);
    if (root->key > key) {
        node->right = root;
        node->left = root->left;
        root->left = nullptr;
    }
    else {
        node->left = root;
        node->right = root->right;
        root->right = nullptr;
    }
    return node;
}

void preOrder(Node* node) {
    if (node != nullptr) {
        cout << node->key << " ";
        preOrder(node->left);
        preOrder(node->right);
    }
}

int main() {
    Node* root = nullptr;
    root = insert(root, 100);
    root = insert(root, 50);
    root = insert(root, 200);
    root = insert(root, 40);
    root = insert(root, 60);
    cout << "Preorder traversal of the modified Splay tree:" << endl;
    preOrder(root);
    return 0;
}
// Java Program for the above approach

class Node {
    public int key;
    public Node left, right;
}

class SplayTree {
    static Node newNode(int key) {
        Node node = new Node();
        node.key = key;
        node.left = node.right = null;
        return node;
    }

    static Node rightRotate(Node x) {
        Node y = x.left;
        x.left = y.right;
        y.right = x;
        return y;
    }

    static Node leftRotate(Node x) {
        Node y = x.right;
        x.right = y.left;
        y.left = x;
        return y;
    }

    static Node splay(Node root, int key) {
        if (root == null || root.key == key)
            return root;

        if (root.key > key) {
            if (root.left == null)
                return root;
            if (root.left.key > key) {
                root.left.left = splay(root.left.left, key);
                root = rightRotate(root);
            }
            else if (root.left.key < key) {
                root.left.right = splay(root.left.right, key);
                if (root.left.right != null)
                    root.left = leftRotate(root.left);
            }
            return (root.left == null) ? root : rightRotate(root);
        }
        else {
            if (root.right == null)
                return root;
            if (root.right.key > key) {
                root.right.left = splay(root.right.left, key);
                if (root.right.left != null)
                    root.right = rightRotate(root.right);
            }
            else if (root.right.key < key) {
                root.right.right = splay(root.right.right, key);
                root = leftRotate(root);
            }
            return (root.right == null) ? root : leftRotate(root);
        }
    }

    static Node insert(Node root, int key) {
        if (root == null)
            return newNode(key);

        root = splay(root, key);

        if (root.key == key)
            return root;

        Node node = newNode(key);
        if (root.key > key) {
            node.right = root;
            node.left = root.left;
            root.left = null;
        }
        else {
            node.left = root;
            node.right = root.right;
            root.right = null;
        }
        return node;
    }

    static void preOrder(Node node) {
        if (node != null) {
            System.out.println();
            System.out.print(node.key + " ");
            preOrder(node.left);
            preOrder(node.right);
        }
    }

    public static void main(String[] args) {
        Node root = null;
        root = insert(root, 100);
        root = insert(root, 50);
        root = insert(root, 200);
        root = insert(root, 40);
        root = insert(root, 60);
        System.out.println("Preorder traversal of the modified Splay tree:");
        preOrder(root);
    }
}


// This code is contributed by princekumaras
class Node:
    def __init__(self, key):
        self.key = key
        self.left = None
        self.right = None

def new_node(key):
    return Node(key)

def right_rotate(x):
    y = x.left
    x.left = y.right
    y.right = x
    return y

def left_rotate(x):
    y = x.right
    x.right = y.left
    y.left = x
    return y

def splay(root, key):
    if root is None :
      return  new_node(key)
    if root.key == key:
        return root

    if root.key > key:
        if root.left is None:
            return root
        if root.left.key > key:
            root.left.left = splay(root.left.left, key)
            root = right_rotate(root)
        elif root.left.key < key:
            root.left.right = splay(root.left.right, key)
            if root.left.right:
                root.left = left_rotate(root.left)
        return root.left if root.left is None else right_rotate(root)
    else:
        if root.right is None:
            return root
        if root.right.key > key:
            root.right.left = splay(root.right.left, key)
            if root.right.left:
                root.right = right_rotate(root.right)
        elif root.right.key < key:
            root.right.right = splay(root.right.right, key)
            root = left_rotate(root)
        return root.right if root.right is None else left_rotate(root)

def insert(root, key):
    if root is None:
        return new_node(key)

    root = splay(root, key)

    if root.key == key:
        return root

    node = new_node(key)
    if root.key > key:
        node.right = root
        node.left = root.left
        root.left = None
    else:
        node.left = root
        node.right = root.right
        root.right = None
    return node

def pre_order(node):
    if node:
        print(node.key, end=" ")
        pre_order(node.left)
        pre_order(node.right)

if __name__ == "__main__":
    root = None
    root = insert(root, 100)
    root = insert(root, 50)
    root = insert(root, 200)
    root = insert(root, 40)
    root = insert(root, 60)
    print("Preorder traversal of the modified Splay tree:")
    pre_order(root)
// C# program for the above approach

using System;

class Node {
    public int key;
    public Node left, right;
}

class SplayTree {
    static Node newNode(int key)
    {
        Node node = new Node();
        node.key = key;
        node.left = node.right = null;
        return node;
    }

    static Node rightRotate(Node x)
    {
        Node y = x.left;
        x.left = y.right;
        y.right = x;
        return y;
    }

    static Node leftRotate(Node x)
    {
        Node y = x.right;
        x.right = y.left;
        y.left = x;
        return y;
    }

    static Node splay(Node root, int key)
    {
        if (root == null || root.key == key)
            return root;

        if (root.key > key) {
            if (root.left == null)
                return root;
            if (root.left.key > key) {
                root.left.left = splay(root.left.left, key);
                root = rightRotate(root);
            }
            else if (root.left.key < key) {
                root.left.right
                    = splay(root.left.right, key);
                if (root.left.right != null)
                    root.left = leftRotate(root.left);
            }
            return (root.left == null) ? root
                                       : rightRotate(root);
        }
        else {
            if (root.right == null)
                return root;
            if (root.right.key > key) {
                root.right.left
                    = splay(root.right.left, key);
                if (root.right.left != null)
                    root.right = rightRotate(root.right);
            }
            else if (root.right.key < key) {
                root.right.right
                    = splay(root.right.right, key);
                root = leftRotate(root);
            }
            return (root.right == null) ? root
                                        : leftRotate(root);
        }
    }

    static Node insert(Node root, int key)
    {
        if (root == null)
            return newNode(key);

        root = splay(root, key);

        if (root.key == key)
            return root;

        Node node = newNode(key);
        if (root.key > key) {
            node.right = root;
            node.left = root.left;
            root.left = null;
        }
        else {
            node.left = root;
            node.right = root.right;
            root.right = null;
        }
        return node;
    }

    static void preOrder(Node node)
    {
        if (node != null) {
            Console.Write(node.key + " ");
            preOrder(node.left);
            preOrder(node.right);
        }
    }

    public static void Main(string[] args)
    {
        Node root = null;
        root = insert(root, 100);
        root = insert(root, 50);
        root = insert(root, 200);
        root = insert(root, 40);
        root = insert(root, 60);
        Console.WriteLine(
            "Preorder traversal of the modified Splay tree:");
        preOrder(root);
    }
}

// This code is contributed by Prince Kumar
// Javascript code addition 

class Node {
  constructor(key) {
    this.key = key;
    this.left = null;
    this.right = null;
  }
}

class SplayTree {
  static newNode(key) {
    const node = new Node(key);
    return node;
  }

  static rightRotate(x) {
    const y = x.left;
    x.left = y.right;
    y.right = x;
    return y;
  }

  static leftRotate(x) {
    const y = x.right;
    x.right = y.left;
    y.left = x;
    return y;
  }

  static splay(root, key) {
    if (root == null || root.key == key) {
      return root;
    }

    if (root.key > key) {
      if (root.left == null) {
        return root;
      }

      if (root.left.key > key) {
        root.left.left = SplayTree.splay(root.left.left, key);
        root = SplayTree.rightRotate(root);
      } else if (root.left.key < key) {
        root.left.right = SplayTree.splay(root.left.right, key);
        if (root.left.right != null) {
          root.left = SplayTree.leftRotate(root.left);
        }
      }
      return root.left == null ? root : SplayTree.rightRotate(root);
    } else {
      if (root.right == null) {
        return root;
      }

      if (root.right.key > key) {
        root.right.left = SplayTree.splay(root.right.left, key);
        if (root.right.left != null) {
          root.right = SplayTree.rightRotate(root.right);
        }
      } else if (root.right.key < key) {
        root.right.right = SplayTree.splay(root.right.right, key);
        root = SplayTree.leftRotate(root);
      }
      return root.right == null ? root : SplayTree.leftRotate(root);
    }
  }

  static insert(root, key) {
    if (root == null) {
      return SplayTree.newNode(key);
    }

    root = SplayTree.splay(root, key);

    if (root.key == key) {
      return root;
    }

    const node = SplayTree.newNode(key);
    if (root.key > key) {
      node.right = root;
      node.left = root.left;
      root.left = null;
    } else {
      node.left = root;
      node.right = root.right;
      root.right = null;
    }
    return node;
  }

  static preOrder(node) {
    if (node != null) {
      console.log(node.key + " ");
      SplayTree.preOrder(node.left);
      SplayTree.preOrder(node.right);
    }
  }
}

let root = null;
root = SplayTree.insert(root, 100);
root = SplayTree.insert(root, 50);
root = SplayTree.insert(root, 200);
root = SplayTree.insert(root, 40);
root = SplayTree.insert(root, 60);
console.log("Preorder traversal of the modified Splay tree:");
SplayTree.preOrder(root);

// The code is contributed by Nidhi goel. 

Output
Preorder traversal of the modified Splay tree: 

Drawbacks of splay tree data structure:

Applications of the splay tree:

Sure, here are some advantages and disadvantages of splay trees, as well as some recommended books for learning more about the topic:

Advantages of Splay Trees:

Splay trees have amortized time complexity of O(log n) for many operations, making them faster than many other balanced tree data structures in some cases.
Splay trees are self-adjusting, meaning that they automatically balance themselves as items are inserted and removed. This can help to avoid the performance degradation that can occur when a tree becomes unbalanced.

Disadvantages of Splay Trees:

Splay trees can have worst-case time complexity of O(n) for some operations, making them less predictable than other balanced tree data structures like AVL trees or red-black trees.
Splay trees may not be suitable for certain applications where predictable performance is required.

Recommended books on Splay Trees:

"Data Structures and Algorithm Analysis in Java" by Mark Allen Weiss
"Introduction to Algorithms" by Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein
"Data Structures and Algorithms in C++" by Michael T. Goodrich and Roberto Tamassia

Conclusion:

In conclusion, Splay Trees are a dynamic self-balancing binary search tree data structure that provides an efficient way of searching, inserting, and deleting elements. They differ from traditional balanced binary search trees like AVL and Red-Black trees, as they reorganize the tree after each operation to bring the recently accessed node to the root. This helps to reduce the height of the tree and results in faster operations. Splay Trees are highly flexible and can be adapted to various use cases. Although they may have a higher overhead in terms of rotations, their simplicity and versatility make them useful tools for solving a wide range of problems.

Article Tags :