0% found this document useful (0 votes)
12 views60 pages

DS Module-4

This document covers the concepts of binary trees, including definitions, representations, and various traversal techniques such as inorder, preorder, and postorder. It also discusses binary search trees (BST), their properties, and methods for inserting and searching for nodes. The document includes Java implementations for these data structures and their operations, along with time and space complexity analyses.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views60 pages

DS Module-4

This document covers the concepts of binary trees, including definitions, representations, and various traversal techniques such as inorder, preorder, and postorder. It also discusses binary search trees (BST), their properties, and methods for inserting and searching for nodes. The document includes Java implementations for these data structures and their operations, along with time and space complexity analyses.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 60

MODULE-4 (10 Hours)

Trees: Vocabulary and Definitions, Binary Tree, Implementation, Binary Tree


Traversal, Binary Search Tree, Implementation, Balanced Search Trees: AVL
Trees, Implementation, Splay Trees, Red- Black Trees.

Binary tree vocabulary and definitions

Binary Tree is defined as a tree data structure where each node has at most 2
children. Since each element in a binary tree can have only 2 children, we typically
name them the left and right child.

Binary Tree Representation

A Binary tree is represented by a pointer to the topmost node (commonly known as the
“root”) of the tree. If the tree is empty, then the value of the root is NULL. Each node of a
Binary Tree contains the following parts:
1. Data
2. Pointer to left child
3. Pointer to right child
Basic Operation On Binary Tree:
 Inserting an element.
 Removing an element.
 Searching for an element.
 Traversing the tree.
Binary Tree (Array implementation)
Given an array that represents a tree in such a way that array indexes are values in
tree nodes and array values give the parent node of that particular index (or node).
The value of the root node index would always be -1 as there is no parent for root.
Construct the standard linked representation of given Binary Tree from this given
representation.
In order to represent a tree using an array, the numbering of nodes can start either
from 0–(n-1) or 1– n, consider the below illustration as follows:

Implementation:
// JAVA implementation of tree using array

// numbering starting from 0 to n-1.

// Importing required classes

import java.io.*;

import java.lang.*;

import java.util.*;
// Class 1

// Helper class (Node class)

public class Tree {

// Main driver method

public static void main(String[] args)

// Creating object of class 2 inside main() method

Array_imp obj = new Array_imp();

// Setting root node

obj.Root("A");

obj.set_Left("B", 0);

obj.set_Right("C", 0);

obj.set_Left("D", 1);

obj.set_Right("E", 1);

obj.set_Right("F", 2);

obj.print_Tree();

// Class 2

// Helper class

class Array_imp {

// Member variables of this class

static int root = 0;

static String[] str = new String[10];


// Method 1

// Creating root node

public void Root(String key) { str[0] = key; }

// Method 2

// Creating left son of root

public void set_Left(String key, int root)

int t = (root * 2) + 1;

if (str[root] == null) {

System.out.printf(

"Can't set child at %d, no parent found\n",

t);

else {

str[t] = key;

// Method 3

// Creating right son of root

public void set_Right(String key, int root)

int t = (root * 2) + 2;

if (str[root] == null) {

System.out.printf(

"Can't set child at %d, no parent found\n",

t);

}
else {

str[t] = key;

// Method 4

// To print our tree

public void print_Tree()

// Iterating using for loop

for (int i = 0; i < 10; i++) {

if (str[i] != null)

System.out.print(str[i]);

else

System.out.print("-");

Output
ABCDE-F---
Time complexity: O(log n) since using heap to create a binary tree
Space complexity: O(1)

Tree Traversal Techniques

Unlike linear data structures (Array, Linked List, Queues, Stacks, etc) which have
only one logical way to traverse them, trees can be traversed in different ways.
A Tree Data Structure can be traversed in following ways:
1. Depth First Search or DFS
1. Inorder Traversal
2. Preorder Traversal
3. Postorder Traversal
2. Level Order Traversal or Breadth First Search or BFS
3. Boundary Traversal
4. Diagonal Traversal

Inorder Traversal:
Algorithm Inorder(tree)
1. Traverse the left subtree, i.e., call Inorder(left->subtree)
2. Visit the root.
3. Traverse the right subtree, i.e., call Inorder(right->subtree)

Code implementation of Inorder traversal.

// Java program for different tree traversals

// Class containing left and right child of current

// node and key value

class Node {

int key;

Node left, right;


public Node(int item)

key = item;

left = right = null;

class BinaryTree {

// Root of Binary Tree

Node root;

BinaryTree() { root = null; }

// Given a binary tree, print its nodes in inorder

void printInorder(Node node)

if (node == null)

return;

// First recur on left child

printInorder(node.left);

// Then print the data of node

System.out.print(node.key + " ");

// Now recur on right child

printInorder(node.right);

// Driver code
public static void main(String[] args)

BinaryTree tree = new BinaryTree();

tree.root = new Node(1);

tree.root.left = new Node(2);

tree.root.right = new Node(3);

tree.root.left.left = new Node(4);

tree.root.left.right = new Node(5);

// Function call

System.out.println(

"Inorder traversal of binary tree is ");

tree.printInorder(tree.root);

Output
Inorder traversal of binary tree is
4 2 5 1 3
Time Complexity: O(N)
Auxiliary Space: If we don’t consider the size of the stack for function calls then
O(1) otherwise O(h) where h is the height of the tree.
Preorder Traversal:
Algorithm Preorder(tree)
1. Visit the root.
2. Traverse the left subtree, i.e., call Preorder(left->subtree)
3. Traverse the right subtree, i.e., call Preorder(right->subtree)

Uses of Preorder:

Preorder traversal is used to create a copy of the tree. Preorder traversal is also used
to get prefix expressions on an expression tree.
Code implementation of Preorder traversal:
// Java program for different tree traversals
// Class containing left and right child of current

// node and key value

class Node {

int key;

Node left, right;

public Node(int item)

key = item;

left = right = null;

class BinaryTree {

// Root of Binary Tree

Node root;

BinaryTree() { root = null; }

// Given a binary tree, print its nodes in preorder

void printPreorder(Node node)

if (node == null)

return;

// First print data of node

System.out.print(node.key + " ");

// Then recur on left subtree


printPreorder(node.left);

// Now recur on right subtree

printPreorder(node.right);

// Driver code

public static void main(String[] args)

BinaryTree tree = new BinaryTree();

tree.root = new Node(1);

tree.root.left = new Node(2);

tree.root.right = new Node(3);

tree.root.left.left = new Node(4);

tree.root.left.right = new Node(5);

// Function call

System.out.println(

"Preorder traversal of binary tree is ");

tree.printPreorder(tree.root);

Output
Preorder traversal of binary tree is
1 2 4 5 3
Time Complexity: O(N)
Auxiliary Space: If we don’t consider the size of the stack for function calls then
O(1) otherwise O(h) where h is the height of the tree.

Postorder Traversal:
Algorithm Postorder(tree)
1. Traverse the left subtree, i.e., call Postorder(left->subtree)
2. Traverse the right subtree, i.e., call Postorder(right->subtree)
3. Visit the root

Uses of Postorder:

Postorder traversal is used to delete the tree. Please see the question for the deletion of
a tree for details. Postorder traversal is also useful to get the postfix expression of an
expression tree
Below is the implementation of the above traversal methods:

// Java program for different tree traversals

// Class containing left and right child of current

// node and key value

class Node {

int key;

Node left, right;

public Node(int item)

key = item;

left = right = null;

class BinaryTree {

// Root of Binary Tree

Node root;

BinaryTree() { root = null; }


// Given a binary tree, print its nodes according to the

// "bottom-up" postorder traversal.

void printPostorder(Node node)

if (node == null)

return;

// First recur on left subtree

printPostorder(node.left);

// Then recur on right subtree

printPostorder(node.right);

// Now deal with the node

System.out.print(node.key + " ");

// Driver code

public static void main(String[] args)

BinaryTree tree = new BinaryTree();

tree.root = new Node(1);

tree.root.left = new Node(2);

tree.root.right = new Node(3);

tree.root.left.left = new Node(4);

tree.root.left.right = new Node(5);

// Function call

System.out.println(

"Postorder traversal of binary tree is ");

tree.printPostorder(tree.root);
}

Binary Search tree

What is a Binary Search tree?

A binary search tree follows some order to arrange the elements. In a Binary
search tree, the value of left node must be smaller than the parent node, and the
value of right node must be greater than the parent node. This rule is applied
recursively to the left and right subtrees of the root.

In the above figure, we can observe that the root node is 40, and all the nodes of the
left subtree are smaller than the root node, and all the nodes of the right subtree are
greater than the root node.

Similarly, we can see the left child of root node is greater than its left child and smaller
than its right child. So, it also satisfies the property of binary search tree. Therefore, we
can say that the tree in the above image is a binary search tree.

Suppose if we change the value of node 35 to 55 in the above tree, check whether the
tree will be binary search tree or not.
In the above tree, the value of root node is 40, which is greater than its left child 30
but smaller than right child of 30, i.e., 55. So, the above tree does not satisfy the
property of Binary search tree. Therefore, the above tree is not a binary search tree.

Insertion in Binary Search Tree (BST)


Given a BST, the task is to insert a new node in this BST.
Example:
How to Insert a value in a Binary Search Tree:
A new key is always inserted at the leaf by maintaining the property of the binary
search tree. We start searching for a key from the root until we hit a leaf node. Once
a leaf node is found, the new node is added as a child of the leaf node. The below
steps are followed while we try to insert a node into a binary search tree:
 Check the value to be inserted (say X) with the value of the current node
(say val) we are in:
 If X is less than val move to the left subtree.
 Otherwise, move to the right subtree.
 Once the leaf node is reached, insert X to its right or left based on the
relation between X and the leaf node’s value.

Insertion in Binary Search Tree using Recursion:


// Java program to demonstrate

// insert operation in binary

// search tree

import java.io.*;

public class BinarySearchTree {

// Class containing left


// and right child of current node

// and key value

class Node {

int key;

Node left, right;

public Node(int item)

key = item;

left = right = null;

// Root of BST

Node root;

// Constructor

BinarySearchTree() { root = null; }

BinarySearchTree(int value) { root = new Node(value); }

// This method mainly calls insertRec()

void insert(int key) { root = insertRec(root, key); }

// A recursive function to

// insert a new key in BST

Node insertRec(Node root, int key)

// If the tree is empty,

// return a new node

if (root == null) {
root = new Node(key);

return root;

// Otherwise, recur down the tree

else if (key < root.key)

root.left = insertRec(root.left, key);

else if (key > root.key)

root.right = insertRec(root.right, key);

// Return the (unchanged) node pointer

return root;

// This method mainly calls InorderRec()

void inorder() { inorderRec(root); }

// A utility function to

// do inorder traversal of BST

void inorderRec(Node root)

if (root != null) {

inorderRec(root.left);

System.out.print(root.key + " ");

inorderRec(root.right);

// Driver Code

public static void main(String[] args)

{
BinarySearchTree tree = new BinarySearchTree();

/* Let us create following BST

50

/ \

30 70

/\/\

20 40 60 80 */

tree.insert(50);

tree.insert(30);

tree.insert(20);

tree.insert(40);

tree.insert(70);

tree.insert(60);

tree.insert(80);

// Print inorder traversal of the BST

tree.inorder();

Output
20 30 40 50 60 70 80
Time Complexity:
 The worst-case time complexity of insert operations is O(h) where h is the
height of the Binary Search Tree.
 In the worst case, we may have to travel from the root to the deepest leaf
node. The height of a skewed tree may become n and the time complexity
of insertion operation may become O(n).
Auxiliary Space: The auxiliary space complexity of insertion into a binary search
tree is O(1)

Insertion in Binary Search Tree using Iterative approach:


Instead of using recursion, we can also implement the insertion operation iteratively
using a while loop. Below is the implementation using a while loop.
// Java code to implement the insertion

// in binary search tree

import java.io.*;

import java.util.*;

class GFG {

// Driver code

public static void main(String[] args)

BST tree = new BST();

tree.insert(30);

tree.insert(50);

tree.insert(15);

tree.insert(20);

tree.insert(10);

tree.insert(40);

tree.insert(60);

tree.inorder();

class Node {

Node left;

int val;

Node right;

Node(int val) { this.val = val; }

}
class BST {

Node root;

// Function to insert a key

public void insert(int key)

Node node = new Node(key);

if (root == null) {

root = node;

return;

Node prev = null;

Node temp = root;

while (temp != null) {

if (temp.val > key) {

prev = temp;

temp = temp.left;

else if (temp.val < key) {

prev = temp;

temp = temp.right;

if (prev.val > key)

prev.left = node;

else

prev.right = node;

// Function to print the inorder value


public void inorder()

Node temp = root;

Stack<Node> stack = new Stack<>();

while (temp != null || !stack.isEmpty()) {

if (temp != null) {

stack.add(temp);

temp = temp.left;

else {

temp = stack.pop();

System.out.print(temp.val + " ");

temp = temp.right;

Output
10 15 20 30 40 50 60
The time complexity of inorder traversal is O(n), as each node is visited once.
The Auxiliary space is O(n), as we use a stack to store nodes for recursion.

Searching in Binary Search Tree (BST)

Given a BST, the task is to delete a node in this BST.


Algorithm to search for a key in a given Binary Search Tree:
Let’s say we want to search for the number X, We start at the root. Then:
 We compare the value to be searched with the value of the root.
 If it’s equal we are done with the search if it’s smaller we know
that we need to go to the left subtree because in a binary search
tree all the elements in the left subtree are smaller and all the
elements in the right subtree are larger.
 Repeat the above step till no more traversal is possible
 If at any iteration, key is found, return True. Else False.
Program to implement search in BST:
// Java program to search a given key in a given BST

class Node {

int key;

Node left, right;

public Node(int item) {

key = item;

left = right = null;

class BinarySearchTree {

Node root;

// Constructor

BinarySearchTree() {

root = null;

// A utility function to insert

// a new node with given key in BST

Node insert(Node node, int key) {

// If the tree is empty, return a new node

if (node == null) {

node = new Node(key);

return node;

}
// Otherwise, recur down the tree

if (key < node.key)

node.left = insert(node.left, key);

else if (key > node.key)

node.right = insert(node.right, key);

// Return the (unchanged) node pointer

return node;

// Utility function to search a key in a BST

Node search(Node root, int key) {

// Base Cases: root is null or key is present at root

if (root == null || root.key == key)

return root;

// Key is greater than root's key

if (root.key < key)

return search(root.right, key);

// Key is smaller than root's key

return search(root.left, key);

// Driver Code

public static void main(String[] args) {

BinarySearchTree tree = new BinarySearchTree();

// Inserting nodes

tree.root = tree.insert(tree.root, 50);

tree.insert(tree.root, 30);
tree.insert(tree.root, 20);

tree.insert(tree.root, 40);

tree.insert(tree.root, 70);

tree.insert(tree.root, 60);

tree.insert(tree.root, 80);

// Key to be found

int key = 6;

// Searching in a BST

if (tree.search(tree.root, key) == null)

System.out.println(key + " not found");

else

System.out.println(key + " found");

key = 60;

// Searching in a BST

if (tree.search(tree.root, key) == null)

System.out.println(key + " not found");

else

System.out.println(key + " found");

Output
6 not found
60 found
Time complexity: O(h), where h is the height of the BST.
Auxiliary Space: O(h), where h is the height of the BST. This is because the
maximum amount of space needed to store the recursion stack would be h.
Deletion in Binary Search Tree (BST)

Implementation of Deletion operation in a BST:

// Java program to implement optimized delete in BST.

import java.util.*;

class Node {

int key;

Node left, right;

// A utility function to create a new BST node

Node(int item) {

key = item;

left = right = null;

class BST {

Node root;

// A utility function to do inorder traversal of BST

void inorder(Node root) {

if (root != null) {

inorder(root.left);

System.out.print(root.key + " ");

inorder(root.right);

/* A utility function to insert a new node with given key in


* BST */

Node insert(Node node, int key) {

/* If the tree is empty, return a new node */

if (node == null)

return new Node(key);

/* Otherwise, recur down the tree */

if (key < node.key)

node.left = insert(node.left, key);

else if (key > node.key)

node.right = insert(node.right, key);

/* return the (unchanged) node pointer */

return node;

/* Given a binary search tree and a key, this function

deletes the key and returns the new root */

Node deleteNode(Node root, int key) {

// Base case

if (root == null)

return root;

// Recursive calls for ancestors of

// node to be deleted

if (root.key > key) {

root.left = deleteNode(root.left, key);

return root;

} else if (root.key < key) {

root.right = deleteNode(root.right, key);

return root;
}

// We reach here when root is the node

// to be deleted.

// If one of the children is empty

if (root.left == null) {

Node temp = root.right;

return temp;

} else if (root.right == null) {

Node temp = root.left;

return temp;

// If both children exist

else {

Node succParent = root;

// Find successor

Node succ = root.right;

while (succ.left != null) {

succParent = succ;

succ = succ.left;

// Delete successor. Since successor

// is always left child of its parent

// we can safely make successor's right

// right child as left of its parent.

// If there is no succ, then assign


// succ.right to succParent.right

if (succParent != root)

succParent.left = succ.right;

else

succParent.right = succ.right;

// Copy Successor Data to root

root.key = succ.key;

// Delete Successor and return root

return root;

// Driver Code

public static void main(String[] args) {

BST tree = new BST();

/* Let us create following BST

50

/ \

30 70

/\/\

20 40 60 80 */

tree.root = tree.insert(tree.root, 50);

tree.insert(tree.root, 30);

tree.insert(tree.root, 20);

tree.insert(tree.root, 40);

tree.insert(tree.root, 70);

tree.insert(tree.root, 60);
System.out.print("Original BST: ");

tree.inorder(tree.root);

System.out.println("\n\nDelete a Leaf Node: 20");

tree.root = tree.deleteNode(tree.root, 20);

System.out.print("Modified BST tree after deleting Leaf Node:\n");

tree.inorder(tree.root);

System.out.println("\n\nDelete Node with single child: 70");

tree.root = tree.deleteNode(tree.root, 70);

System.out.print("Modified BST tree after deleting single child


Node:\n");

tree.inorder(tree.root);

System.out.println("\n\nDelete Node with both child: 50");

tree.root = tree.deleteNode(tree.root, 50);

System.out.print("Modified BST tree after deleting both child


Node:\n");

tree.inorder(tree.root);

Output
Original BST: 20 30 40 50 60 70

Delete a Leaf Node: 20


Modified BST tree after deleting Leaf Node:
30 40 50 60 70

Delete Node with single child: 70


Modified BST tree after deleting single child No...
Time Complexity: O(h), where h is the height of the BST.
Auxiliary Space: O(n).
Binary Search Tree (BST) Traversals – Inorder, Preorder,
Post Order
Below is the implementation of the inorder traversal.
// Java code to implement the approach

import java.io.*;

// Class describing a node of tree

class Node {

int data;

Node left;

Node right;

Node(int v)

this.data = v;

this.left = this.right = null;

class GFG {

// Inorder Traversal

public static void printInorder(Node node)

if (node == null)

return;

// Traverse left subtree

printInorder(node.left);

// Visit node
System.out.print(node.data + " ");

// Traverse right subtree

printInorder(node.right);

// Driver Code

public static void main(String[] args)

// Build the tree

Node root = new Node(100);

root.left = new Node(20);

root.right = new Node(200);

root.left.left = new Node(10);

root.left.right = new Node(30);

root.right.left = new Node(150);

root.right.right = new Node(300);

// Function call

System.out.print("Inorder Traversal: ");

printInorder(root);

Preorder Traversal:
Below is the idea to solve the problem:
At first visit the root then traverse left subtree and then traverse the right subtree.
Follow the below steps to implement the idea:
 Visit the root and print the data.
 Traverse left subtree
 Traverse the right subtree
Below is the implementation of the preorder traversal.
// Java code to implement the approach

import java.io.*;
// Class describing a node of tree

class Node {

int data;

Node left;

Node right;

Node(int v)

this.data = v;

this.left = this.right = null;

class GFG {

// Preorder Traversal

public static void printPreorder(Node node)

if (node == null)

return;

// Visit node

System.out.print(node.data + " ");

// Traverse left subtree

printPreorder(node.left);

// Traverse right subtree

printPreorder(node.right);

}
public static void main(String[] args)

// Build the tree

Node root = new Node(100);

root.left = new Node(20);

root.right = new Node(200);

root.left.left = new Node(10);

root.left.right = new Node(30);

root.right.left = new Node(150);

root.right.right = new Node(300);

// Function call

System.out.print("Preorder Traversal: ");

printPreorder(root);

Output
Preorder Traversal: 100 20 10 30 200 150 300
Time complexity: O(N), Where N is the number of nodes.
Auxiliary Space: O(H), Where H is the height of the tree
Postorder Traversal:
Below is the idea to solve the problem:
At first traverse left subtree then traverse the right subtree and then visit the root.
Follow the below steps to implement the idea:
 Traverse left subtree
 Traverse the right subtree
 Visit the root and print the data.
Below is the implementation of the postorder traversal:
// Java code to implement the approach

import java.io.*;
// Class describing a node of tree

class GFG {

static class Node {

int data;

Node left;

Node right;

Node(int v)

this.data = v;

this.left = this.right = null;

// Preorder Traversal

public static void printPreorder(Node node)

if (node == null)

return;

// Traverse left subtree

printPreorder(node.left);

// Traverse right subtree

printPreorder(node.right);

// Visit node

System.out.print(node.data + " ");

}
public static void main(String[] args)

// Build the tree

Node root = new Node(100);

root.left = new Node(20);

root.right = new Node(200);

root.left.left = new Node(10);

root.left.right = new Node(30);

root.right.left = new Node(150);

root.right.right = new Node(300);

// Function call

System.out.print("Preorder Traversal: ");

printPreorder(root);

Output
PostOrder Traversal: 10 30 20 150 300 200 100
Time complexity: O(N), Where N is the number of nodes.
Auxiliary Space: O(H), Where H is the height of the tree

Balanced Search Trees: AVL Trees

An AVL tree defined as a self-balancing Binary Search Tree (BST) where the
difference between heights of left and right subtrees for any node cannot be more
than one.
The difference between the heights of the left subtree and the right subtree for any
node is known as the balance factor of the node.
The AVL tree is named after its inventors, Georgy Adelson-Velsky and Evgenii
Landis, who published it in their 1962 paper “An algorithm for the organization of
information”.

Example of AVL Trees:


The above tree is AVL because the differences between the heights of left and right
subtrees for every node are less than or equal to 1.

Operations on an AVL Tree:

 Insertion
 Deletion
 Searching [It is similar to performing a search in BST]

Rotating the subtrees in an AVL Tree:

An AVL tree may rotate in one of the following four ways to keep itself balanced:
Left Rotation:
When a node is added into the right subtree of the right subtree, if the tree gets out of
balance, we do a single left rotation.
Right Rotation:
If a node is added to the left subtree of the left subtree, the AVL tree may get out of
balance, we do a single right rotation.

Left-Right Rotation:
A left-right rotation is a combination in which first left rotation takes place after that
right rotation executes.
Right-Left Rotation:
A right-left rotation is a combination in which first right rotation takes place after
that left rotation executes.

Applications of AVL Tree:

1. It is used to index huge records in a database and also to efficiently search


in that.
2. For all types of in-memory collections, including sets and dictionaries,
AVL Trees are used.
3. Database applications, where insertions and deletions are less common but
frequent data lookups are necessary
4. Software that needs optimized search.
5. It is applied in corporate areas and storyline games.

Advantages of AVL Tree:


1. AVL trees can self-balance themselves.
2. It is surely not skewed.
3. It provides faster lookups than Red-Black Trees
4. Better searching time complexity compared to other trees like binary tree.
5. Height cannot exceed log(N), where, N is the total number of nodes in the
tree.

Disadvantages of AVL Tree:

1. It is difficult to implement.
2. It has high constant factors for some of the operations.
3. Less used compared to Red-Black trees.
4. Due to its rather strict balance, AVL trees provide complicated insertion and
removal operations as more rotations are performed.
5. Take more processing for balancing.

Insertion in an AVL Tree


To make sure that the given tree remains AVL after every insertion, we must
augment the standard BST insert operation to perform some re-balancing.
Following are two basic operations that can be performed to balance a BST without
violating the BST property (keys(left) < key(root) < keys(right)).
 Left Rotation
 Right Rotation
T1, T2 and T3 are subtrees of the tree, rooted with y (on the left
side) or x (on the right side)

Follow the steps mentioned below to implement the idea:


 Perform the normal BST insertion.
 The current node must be one of the ancestors of the newly inserted node.
Update the height of the current node.
 Get the balance factor (left subtree height – right subtree height) of the
current node.
 If the balance factor is greater than 1, then the current node is unbalanced
and we are either in the Left Left case or left Right case. To check
whether it is left left case or not, compare the newly inserted key with the
key in the left subtree root.
 If the balance factor is less than -1, then the current node is unbalanced
and we are either in the Right Right case or Right-Left case. To check
whether it is the Right Right case or not, compare the newly inserted key
with the key in the right subtree root.
Below is the implementation of the above approach:
// Java program for insertion in AVL Tree

class Node {

int key, height;

Node left, right;

Node(int d) {

key = d;

height = 1;

class AVLTree {

Node root;

// A utility function to get the height of the tree

int height(Node N) {

if (N == null)

return 0;

return N.height;

}
// A utility function to get maximum of two integers

int max(int a, int b) {

return (a > b) ? a : b;

// A utility function to right rotate subtree rooted with y

// See the diagram given above.

Node rightRotate(Node y) {

Node x = y.left;

Node T2 = x.right;

// Perform rotation

x.right = y;

y.left = T2;

// Update heights

y.height = max(height(y.left), height(y.right)) + 1;

x.height = max(height(x.left), height(x.right)) + 1;

// Return new root

return x;

// A utility function to left rotate subtree rooted with x

// See the diagram given above.

Node leftRotate(Node x) {

Node y = x.right;

Node T2 = y.left;

// Perform rotation
y.left = x;

x.right = T2;

// Update heights

x.height = max(height(x.left), height(x.right)) + 1;

y.height = max(height(y.left), height(y.right)) + 1;

// Return new root

return y;

// Get Balance factor of node N

int getBalance(Node N) {

if (N == null)

return 0;

return height(N.left) - height(N.right);

Node insert(Node node, int key) {

/* 1. Perform the normal BST insertion */

if (node == null)

return (new Node(key));

if (key < node.key)

node.left = insert(node.left, key);

else if (key > node.key)

node.right = insert(node.right, key);

else // Duplicate keys not allowed

return node;
/* 2. Update height of this ancestor node */

node.height = 1 + max(height(node.left),

height(node.right));

/* 3. Get the balance factor of this ancestor

node to check whether this node became

unbalanced */

int balance = getBalance(node);

// If this node becomes unbalanced, then there

// are 4 cases Left Left Case

if (balance > 1 && key < node.left.key)

return rightRotate(node);

// Right Right Case

if (balance < -1 && key > node.right.key)

return leftRotate(node);

// Left Right Case

if (balance > 1 && key > node.left.key) {

node.left = leftRotate(node.left);

return rightRotate(node);

// Right Left Case

if (balance < -1 && key < node.right.key) {

node.right = rightRotate(node.right);

return leftRotate(node);

}
/* return the (unchanged) node pointer */

return node;

// A utility function to print preorder traversal

// of the tree.

// The function also prints height of every node

void preOrder(Node node) {

if (node != null) {

System.out.print(node.key + " ");

preOrder(node.left);

preOrder(node.right);

public static void main(String[] args) {

AVLTree tree = new AVLTree();

/* Constructing tree given in the above figure */

tree.root = tree.insert(tree.root, 10);

tree.root = tree.insert(tree.root, 20);

tree.root = tree.insert(tree.root, 30);

tree.root = tree.insert(tree.root, 40);

tree.root = tree.insert(tree.root, 50);

tree.root = tree.insert(tree.root, 25);

/* The constructed AVL Tree would be

30

/\

20 40

/\ \
10 25 50

*/

System.out.println("Preorder traversal" +

" of constructed tree is : ");

tree.preOrder(tree.root);

Output
Preorder traversal of the constructed AVL tree is
30 20 10 25 40 50
Complexity Analysis
Time Complexity: O(n*log(n)), For Insertion
Auxiliary Space: O(1)

Comparison with Red Black Tree:


The AVL tree and other self-balancing search trees like Red Black are useful to get
all basic operations done in O(log n) time. The AVL trees are more balanced compared
to Red-Black Trees, but they may cause more rotations during insertion and deletion.
So if your application involves many frequent insertions and deletions, then Red Black
trees should be preferred. And if the insertions and deletions are less frequent and
search is the more frequent operation, then the AVL tree should be preferred over Red
Black Tree.

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.
1. 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.
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:

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:
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:

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:
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:

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:
// 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);

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.
Red- Black Trees
Red-Black tree is a binary search tree in which every node is colored with either red
or black. It is a type of self balancing binary search tree. It has a good efficient worst
case running time complexity.

Properties of Red Black Tree:

The Red-Black tree satisfies all the properties of binary search tree in addition to
that it satisfies following additional properties –
1. Root property: The root is black.
2. External property: Every leaf (Leaf is a NULL child of a node) is black in Red-
Black tree.
3. Internal property: The children of a red node are black. Hence possible parent of
red node is a black node.
4. Depth property: All the leaves have the same black depth.
5. Path property: Every simple path from root to descendant leaf node contains same
number of black nodes.
The result of all these above-mentioned properties is that the Red-Black tree is
roughly balanced.
Rules That Every Red-Black Tree Follows:
1. Every node has a color either red or black.
2. The root of the tree is always black.
3. There are no two adjacent red nodes (A red node cannot have a red parent
or red child).
4. Every path from a node (including root) to any of its descendants NULL
nodes has the same number of black nodes.
5. Every leaf (e.i. NULL node) must be colored BLACK.
Why Red-Black Trees?
Most of the BST operations (e.g., search, max, min, insert, delete.. etc) take O(h)
time where h is the height of the BST. The cost of these operations may become
O(n) for a skewed Binary tree. If we make sure that the height of the tree remains
O(log n) after every insertion and deletion, then we can guarantee an upper bound of
O(log n) for all these operations. The height of a Red-Black tree is always O(log n)
where n is the number of nodes in the tree.
“n” is the total number of elements in the red-black tree.
Comparison with AVL Tree:
The AVL trees are more balanced compared to Red-Black Trees, but they may cause
more rotations during insertion and deletion. So if your application involves frequent
insertions and deletions, then Red-Black trees should be preferred. And if the
insertions and deletions are less frequent and search is a more frequent operation,
then AVL tree should be preferred over the Red-Black Tree.
How does a Red-Black Tree ensure balance?
A simple example to understand balancing is, that a chain of 3 nodes is not possible
in the Red-Black tree. We can try any combination of colors and see if all of them
violate the Red-Black tree property.

Interesting points about Red-Black Tree:


1. The black height of the red-black tree is the number of black nodes on a
path from the root node to a leaf node. Leaf nodes are also counted as
black nodes. So, a red-black tree of height h has black height >= h/2.
2. Height of a red-black tree with n nodes is h<= 2 log2(n + 1).
3. All leaves (NIL) are black.
4. The black depth of a node is defined as the number of black nodes from
the root to that node i.e the number of black ancestors.
5. Every red-black tree is a special case of a binary tree.
Black Height of a Red-Black Tree :
Black height is the number of black nodes on a path from the root to a leaf. Leaf
nodes are also counted black nodes. From the above properties 3 and 4, we can
derive, a Red-Black Tree of height h has black-height >= h/2.
Number of nodes from a node to its farthest descendant leaf is no more than twice as
the number of nodes to the nearest descendant leaf.
Every Red Black Tree with n nodes has height <= 2Log2(n+1)
This can be proved using the following facts:
1. For a general Binary Tree, let k be the minimum number of nodes on all
root to NULL paths, then n >= 2 k – 1 (Ex. If k is 3, then n is at least 7).
This expression can also be written as k <= Log 2(n+1).
2. From property 4 of Red-Black trees and above claim, we can say in a Red-
Black Tree with n nodes, there is a root to leaf path with at-most
Log2(n+1) black nodes.
3. From properties 3 and 5 of Red-Black trees, we can claim that the number
of black nodes in a Red-Black tree is at least ⌊ n/2 ⌋ where n is the total
number of nodes.
From the above points, we can conclude the fact that Red Black Tree with n nodes
has a height <= 2Log2(n+1)
Search Operation in Red-black Tree:
As every red-black tree is a special case of a binary tree so the searching algorithm
of a red-black tree is similar to that of a binary tree.
Algorithm:

// Java program to search a given key in a given BST


class Node {

int key;

Node left, right;

public Node(int item) {

key = item;

left = right = null;

class BinarySearchTree {

Node root;

// Constructor

BinarySearchTree() {

root = null;

// A utility function to insert

// a new node with given key in BST

Node insert(Node node, int key) {

// If the tree is empty, return a new node

if (node == null) {

node = new Node(key);

return node;

// Otherwise, recur down the tree

if (key < node.key)

node.left = insert(node.left, key);


else if (key > node.key)

node.right = insert(node.right, key);

// Return the (unchanged) node pointer

return node;

// Utility function to search a key in a BST

Node search(Node root, int key) {

// Base Cases: root is null or key is present at root

if (root == null || root.key == key)

return root;

// Key is greater than root's key

if (root.key < key)

return search(root.right, key);

// Key is smaller than root's key

return search(root.left, key);

// Driver Code

public static void main(String[] args) {

BinarySearchTree tree = new BinarySearchTree();

// Inserting nodes

tree.root = tree.insert(tree.root, 50);

tree.insert(tree.root, 30);

tree.insert(tree.root, 20);

tree.insert(tree.root, 40);

tree.insert(tree.root, 70);
tree.insert(tree.root, 60);

tree.insert(tree.root, 80);

// Key to be found

int key = 6;

// Searching in a BST

if (tree.search(tree.root, key) == null)

System.out.println(key + " not found");

else

System.out.println(key + " found");

key = 60;

// Searching in a BST

if (tree.search(tree.root, key) == null)

System.out.println(key + " not found");

else

System.out.println(key + " found");

Output
6 not found
60 found
Time complexity: O(h), where h is the height of the BST.
Auxiliary Space: O(h), where h is the height of the BST. This is because the
maximum amount of space needed to store the recursion stack would be h

You might also like