Open In App

Introduction to Splay tree data structure

Last Updated : 11 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

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.

  • The key feature of a splay tree is that each time an element is accessed, it is moved to the root of the tree, creating a more balanced structure for subsequent accesses.
  • Splay trees are characterized by their use of rotations, which are local transformations of the tree that change its shape but preserve the order of the elements.
  • Rotations are used to bring the accessed element to the root of the tree, and also to rebalance the tree if it becomes unbalanced after multiple accesses.

Operations in a splay tree:

  • Insertion: To insert a new element into the tree, start by performing a regular binary search tree insertion. Then, apply rotations to bring the newly inserted element to the root of the tree.
  • Deletion: To delete an element from the tree, first locate it using a binary search tree search. Then, if the element has no children, simply remove it. If it has one child, promote that child to its position in the tree. If it has two children, find the successor of the element (the smallest element in its right subtree), swap its key with the element to be deleted, and delete the successor instead.
  • Search: To search for an element in the tree, start by performing a binary search tree search. If the element is found, apply rotations to bring it to the root of the tree. If it is not found, apply rotations to the last node visited in the search, which becomes the new root.
  • Rotation: The rotations used in a splay tree are either a Zig or a Zig-Zig rotation. A Zig rotation is used to bring a node to the root, while a Zig-Zig rotation is used to balance the tree after multiple accesses to elements in the same subtree.

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

  • Zig Rotation: If a node has a right child, perform a right rotation to bring it to the root. If it has a left child, perform a left rotation.
  • Zig-Zig Rotation: If a node has a grandchild that is also its child’s right or left child, perform a double rotation to balance the tree. For example, if the node has a right child and the right child has a left child, perform a right-left rotation. If the node has a left child and the left child has a right child, perform a left-right rotation.
  • Note: The specific implementation details, including the exact rotations used, may vary depending on the exact form of the splay tree.

Rotations in Splay Tree

  • Zig Rotation
  • Zag Rotation
  • Zig – Zig Rotation
  • Zag – Zag Rotation
  • Zig – Zag Rotation
  • Zag – Zig Rotation

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:

C++
#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
// 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
Python3
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#
// 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
// 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:

  • Unbalanced Trees: Splay trees can become unbalanced and inefficient if the tree is repeatedly rotated in the same direction.
  • Memory Usage: Splay trees can use a lot of memory compared to other data structures because each node contains additional information.
  • Complexity: Splay trees can have a high time complexity for basic operations such as insertion and deletion because the trees need to be reorganized after every operation.
  • Reorganization Overhead: The splaying operation required in every operation can be time-consuming and result in a high overhead.
  • Limited Use Cases: Splay trees are not suitable for all data structures and have limited use cases because they don’t handle duplicate keys efficiently.

Applications of the splay tree:

  • Caching: Splay trees can be used to implement cache memory management, where the most frequently accessed items are moved to the top of the tree for quicker access.
  • Database Indexing: Splay trees can be used to index databases for faster searching and retrieval of data.
  • File Systems: Splay trees can be used to store file system metadata, such as the allocation table, directory structure, and file attributes.
  • Data Compression: Splay trees can be used to compress data by identifying and encoding repeating patterns.
  • Text Processing: Splay trees can be used in text processing applications, such as spell-checkers, where words are stored in a splay tree for quick searching and retrieval.
  • Graph Algorithms: Splay trees can be used to implement graph algorithms, such as finding the shortest path in a weighted graph.
  • Online Gaming: Splay trees can be used in online gaming to store and manage high scores, leaderboards, and player statistics.

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.



Similar Reads

Searching in Splay Tree
Splay Tree- Splay tree is a binary search tree. In a splay tree, M consecutive operations can be performed in O (M log N) time. A single operation may require O(N) time but average time to perform M operations will need O (M Log N) time. When a node is accessed, it is moved to the top through a set of operations known as splaying. Splaying techniqu
15+ min read
Deletion in Splay Tree
It is recommended to refer following post as prerequisite of this post. Splay Tree | Set 1 (Search) Following are the different cases to delete a key k from splay tree. If Root is NULL: We simply return the root.Else Splay the given key k. If k is present, then it becomes the new root. If not present, then last accessed leaf node becomes the new ro
15+ min read
Insertion in Splay Tree
It is recommended to refer following post as prerequisite of this post.Splay Tree | Set 1 (Search)As discussed in the previous post, Splay tree is a self-balancing data structure where the last accessed key is always at root. The insert operation is similar to Binary Search Tree insert with additional steps to make sure that the newly inserted key
15+ min read
Static Data Structure vs Dynamic Data Structure
Data structure is a way of storing and organizing data efficiently such that the required operations on them can be performed be efficient with respect to time as well as memory. Simply, Data Structure are used to reduce complexity (mostly the time complexity) of the code. Data structures can be two types : 1. Static Data Structure 2. Dynamic Data
4 min read
Introduction to Finger search tree Data Structure
A finger search tree is a data structure that is designed to allow for efficient search and access of data in a set or a sequence. It is a type of binary search tree that uses a "finger" or a reference to a particular element in the tree to quickly find and retrieve other elements. In this article, we will explore the types, advantages, disadvantag
15+ min read
Introduction to Tree Data Structure
Tree data structure is a specialized data structure to store data in hierarchical manner. It is used to organize and store data in the computer to be used more effectively. It consists of a central node, structural nodes, and sub-nodes, which are connected via edges. We can also say that tree data structure has roots, branches, and leaves connected
15+ min read
Introduction to the Probabilistic Data Structure
Introduction: Probabilistic Data Structures are data structures that provide approximate answers to queries about a large dataset, rather than exact answers. These data structures are designed to handle large amounts of data in real-time, by making trade-offs between accuracy and time and space efficiency. Some common examples of Probabilistic Data
5 min read
Introduction to Universal Hashing in Data Structure
Universal hashing is a technique used in computer science and information theory for designing hash functions. It is a family of hash functions that can be efficiently computed by using a randomly selected hash function from a set of hash functions. The goal of universal hashing is to minimize the chance of collisions between distinct keys, which c
5 min read
Introduction to Hierarchical Data Structure
We have discussed Overview of Array, Linked List, Queue and Stack. In this article following Data Structures are discussed. 5. Binary Tree 6. Binary Search Tree 7. Binary Heap 8. Hashing Binary Tree Unlike Arrays, Linked Lists, Stack, and queues, which are linear data structures, trees are hierarchical data structures. A binary tree is a tree data
13 min read
Introduction to Augmented Data Structure
Data Structures play a significant role in building software and applications but many a times all our requirements are not satisfied using an existing data structure. This is when we modify an existing data structure according to our needs. This article will provide a brief introduction about when and how to Augment a Data Structure. Table of Cont
10 min read
Article Tags :
Practice Tags :