Tree


tree data structure is a hierarchical structure that is used to represent and organize data in a way that is easy to navigate and search. It is a collection of nodes that are connected by edges and has a hierarchical relationship between the nodes. The topmost node of the tree is called the root, and the nodes below it are called the child nodes. Each node can have multiple child nodes, and these child nodes can also have their own child nodes, forming a recursive structure.

This data structure is a specialized method 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 with one another. 

Introduction to Tree - Data Structure and Algorithm Tutorials

Why Tree is considered a non-linear data structure?

The data in a tree are not stored in a sequential manner i.e, they are not stored linearly. Instead, they are arranged on multiple levels or we can say it is a hierarchical structure. For this reason, the tree is considered to be a non-linear data structure.

Basic Terminologies In Tree Data Structure:

  • Parent Node: The node which is a predecessor of a node is called the parent node of that node. {B} is the parent node of {D, E}.
  • Child Node: The node which is the immediate successor of a node is called the child node of that node. Examples: {D, E} are the child nodes of {B}.
  • Root Node: The topmost node of a tree or the node which does not have any parent node is called the root node. {A} is the root node of the tree. A non-empty tree must contain exactly one root node and exactly one path from the root to all other nodes of the tree.
  • Leaf Node or External Node: The nodes which do not have any child nodes are called leaf nodes. {K, L, M, N, O, P} are the leaf nodes of the tree.
  • Ancestor of a Node: Any predecessor nodes on the path of the root to that node are called Ancestors of that node. {A,B} are the ancestor nodes of the node {E}
  • Descendant: Any successor node on the path from the leaf node to that node. {E,I} are the descendants of the node {B}.
  • Sibling: Children of the same parent node are called siblings. {D,E} are called siblings.
  • Level of a node: The count of edges on the path from the root node to that node. The root node has level 0.
  • Internal node: A node with at least one child is called Internal Node.
  • Neighbour of a Node: Parent or child nodes of that node are called neighbors of that node.
  • Subtree: Any node of the tree along with its descendant.

Properties of a Tree:

  • Number of edges: An edge can be defined as the connection between two nodes. If a tree has N nodes then it will have (N-1) edges. There is only one path from each node to any other node of the tree.
  • Depth of a node: The depth of a node is defined as the length of the path from the root to that node. Each edge adds 1 unit of length to the path. So, it can also be defined as the number of edges in the path from the root of the tree to the node.
  • Height of a node: The height of a node can be defined as the length of the longest path from the node to a leaf node of the tree.
  • Height of the Tree: The height of a tree is the length of the longest path from the root of the tree to a leaf node of the tree.
  • Degree of a Node: The total count of subtrees attached to that node is called the degree of the node. The degree of a leaf node must be 0. The degree of a tree is the maximum degree of a node among all the nodes in the tree.

Some more properties are:

  • Traversing in a tree is done by depth first search and breadth first search algorithm.
  • It has no loop and no circuit
  • It has no self-loop 
  • Its hierarchical model.

Syntax:
struct Node { 
   int data;
   struct Node *left_child;
   struct Node *right_child;
};

Basic Operation Of Tree:

Create – create a tree in data structure.
Insert − Inserts data in a tree.
Search − Searches specific data  in a tree to check it is present or not.
Preorder Traversal – perform Traveling a tree in a pre-order manner in data structure (root-> left node -> right node)
In order Traversal –
perform Traveling a tree in an in-order manner (left node -> root - > right node)
Post order Traversal –
perform Traveling a tree in a post-order manner (left node -> right node -> root)

Binary Tree

 What is a Binary Tree?

A binary tree is a tree data structure in which each node can have at most two children, which are referred to as the left child and the right child. The topmost node in a binary tree is called the root, and the bottom-most nodes are called leaves.

Representation of Binary Tree:

Each node in the tree contains the following:

  • Data
  • Pointer to the left child
  • Pointer to the right child
Binary Tree
• The root node of this binary tree is A.
• The left sub tree of the root node, which we denoted by LA , is the set 
LA = {B,D,E,G} and the right sub tree of the root node, RA is the set RA={C,F,H}
• The root node of LA is node B, the root node of RA is C and so on

Binary Tree Properties
• If a binary tree contains m nodes at level L, it contains at most 2m
nodes at level L+1
• Since a binary tree can contain at most 1 node at level 0 (the root), it
contains at most 2L nodes at level L. 

Types of Binary Tree
• Complete binary tree
• Strictly binary tree
• Almost complete binary tree

Strictly binary tree
• If every non-leaf node in a binary tree has nonempty left and right sub-trees, then
such a tree is called a strictly binary tree.
• Or, to put it another way, all of the nodes in a strictly binary tree are of degree zero
or two, never degree one.
• A strictly binary tree with N leaves always contains 2N – 1 nodes.

Complete binary tree
• A complete binary tree is a binary tree in which every level, except possibly the last, is completely filled, and all nodes are as far left as possible.
• A complete binary tree of depth d is called strictly binary tree if all of whose leaves are at level d.
• A complete binary tree has 2 d nodes at every depth d and 2-1 non leaf nodes

Almost complete binary tree
• An almost complete binary tree is a tree where for a right child, there is always a
left child, but for a left child there may not be a right child. 

Basic Operations On Binary Tree:

  • Inserting an element.
  • Removing an element.
  • Searching for an element.
  • Deletion for an element.
  • Traversing an element.

Binary Tree Traversals:

Tree Traversal algorithms can be classified broadly into two categories:

  • Depth-First Search (DFS) Algorithms
  • Breadth-First Search (BFS) Algorithms

Tree Traversal using Depth-First Search (DFS) algorithm can be further classified into three categories:

  • Preorder Traversal (current-left-right): Visit the current node before visiting any nodes inside the left or right subtrees. Here, the traversal is root – left child – right child. It means that the root node is traversed first then its left child and finally the right child.

  • Inorder Traversal (left-current-right): Visit the current node after visiting all nodes inside the left subtree but before visiting any node within the right subtree. Here, the traversal is left child – root – right child.  It means that the left child is traversed first then its root node and finally the right child.
  • Postorder Traversal (left-right-current): Visit the current node after visiting all the nodes of the left and right subtrees.  Here, the traversal is left child – right child – root.  It means that the left child has traversed first then the right child and finally its root node.
Pre -order Pseudocode
struct Node
{
char data;
Node *left;
Node *right;
}
void Preorder(Node *root) {
if (root==NULL) return;
printf (“%c”, root->data);
Preorder(root->left);
Preorder(root->right);
}

In-order Pseudocode
struct Node
{
char data;
Node *left;
Node *right;
}
void Inorder
(Node *root)
{
if (root==NULL) return;
Inorder(root->left);
printf (“%c”, root->data);
Inorder(root->right);
}

Post-order Pseudocode
struct Node
{
char data;
Node *left;
Node *right;
}
void Postorder
(Node *root)
{
if (root==NULL) return;
Postorder(root->left);
Postorder(root->right);
printf (“%c”, root->data);
}

Tree Traversal using Breadth-First Search (BFS) algorithm can be further classified into one category:

  • Level Order Traversal:  Visit nodes level-by-level and left-to-right fashion at the same level. Here, the traversal is level-wise. It means that the most left child has traversed first and then the other children of the same level from left to right have traversed. 

Let us traverse the following tree with all four traversal methods:

Binary Tree

Binary Tree

Pre-order Traversal of the above tree: 1-2-4-5-3-6-7
In-order Traversal of the above tree: 4-2-5-1-6-3-7
Post-order Traversal of the above tree: 4-5-2-6-7-3-1
Level-order Traversal of the above tree: 1-2-3-4-5-6-7

 Binary Search Tree(BST)

• A binary search tree (BST) is a binary tree that is either empty or in which every node contains a key (value) and satisfies the following conditions:

• All keys in the left sub-tree of the root are smaller than the key in the root node

• All keys in the right sub-tree of the root are greater than the key in the root node

• The left and right sub-trees of the root are again binary search trees

• A binary search tree is basically a binary tree, and therefore it can be traversed in inorder, preorder and postorder.

• If we traverse a binary search tree in inorder and print the identifiers contained in the nodes of the tree, we get a sorted list of identifiers in ascending order.

Why Binary Search Tree?
• Let us consider a problem of searching a list.
• If a list is ordered searching becomes faster if we use contiguous list(array).
• But if we need to make changes in the list, such as inserting new entries or deleting old entries, (SLOWER!!!!) because insertion and deletion in a contiguous list requires moving many of the entries every time.

So we may think of using a linked list because it permits insertion and deletion to be carried out by adjusting only few pointers.
• But in an n-linked list, there is no way to move through the list other than one node at a time, permitting only sequential access.
• Binary trees provide an excellent solution to this problem. By making the entries of an ordered list into the nodes of a binary search tree, we find that we can search for a key in O(logn)

Time Complexity 
                        Array                            Linked List                    BST 
Search            O(n)                              O(n)                                O(logn) 
Insert              O(1)                              O(1)                               O(logn) 
Remove          O(n)                              O(n)                               O(logn)

Operations on Binary Search Tree (BST)
• Following operations can be done in BST:
• Search(k, T): Search for key k in the tree T. If k is found in some node of tree then return true otherwise return false.
• Insert(k, T): Insert a new node with value k in the info field in the tree T such that the property of BST is maintained.
• Delete(k, T):Delete a node with value k in the info field from the tree T such that the property of BST is maintained.
• FindMin(T), FindMax(T): Find minimum and maximum element from the given nonempty BST. 

Searching Through The BST
• Compare the target value with the element in the root node
 If the target value is equal, the search is successful.
If target value is less, search the left subtree.
If target value is greater, search the right subtree.
If the subtree is empty, the search is unsuccessful.

Insertion of a node in BST
• To insert a new item in a tree, we must first verify that its key is different from those of existing elements.
• If a new value is less, than the current node's value, go to the left subtree, else go to the right subtree.
• Following this simple rule, the algorithm reaches a node, which has no left or right subtree.
• By the moment a place for insertion is found, we can say for sure, that a new value has no duplicate in the tree.

Algorithm for insertion in BST
• Check, whether value in current node and a new value are equal. If so, duplicate is found. Otherwise,
• if a new value is less, than the node's value:
• if a current node has no left child, place for insertion has been found;
• otherwise, handle the left child with the same algorithm.
• if a new value is greater, than the node's value:
• if a current node has no right child, place for insertion has been found;
• otherwise, handle the right child with the same algorithm.


Deleting a node from the BST

When we delete a node, three possibilities arise. 

1) Node to be deleted is the leaf: Simply remove it from the tree. 

              50                                      50
            /     \         delete(20)          /   \
         30      70       ———>      30     70 
         /  \    /  \                                \     /  \ 
     20   40  60   80                        40  60   80

2) Node to be deleted has only one child: Copy the child to the node and delete the node. 

              50                                   50
           /     \         delete(30)        /   \
        30      70       ———>    40     70 
           \    /  \                                    /  \ 
         40  60   80                            60   80

3) Node to be deleted has two children: Find inorder successor of the node. Copy contents of the inorder successor to the node and delete the inorder successor.

Note: Inorder predecessor can also be used. 

              50                                    60
           /     \          delete(50)        /   \
        40      70       ———>     40    70 
                 /  \                                      \ 
              60   80                                   80

Note: Inorder successor is needed only when the right child is not empty. In this particular case, in-order successor can be obtained by finding the minimum value in the right child of the node.

Alogirthm for deletion of a node in BST

Follow the below steps to solve the problem:

  • If the root is NULL, then return root (Base case)
  • If the key is less than the root’s value, then set root->left = deleteNode(root->left, key)
  • If the key is greater than the root’s value, then set root->right = deleteNode(root->right, key)
  • Else check
    • If the root is a leaf node then return null
    • else if it has only the left child, then return the left child
    • else if it has only the right child, then return the right child
    • else set the value of root as of its inorder successor and recur to delete the node with the value of the inorder successor
  • Return

Ex: Implementation of binary tree

#include <stdlib.h>

#include <iostream>

using namespace std;

struct node {

  int data;

  struct node *left;

  struct node *right;

};

// New node creation

struct node *newNode(int data) {

  struct node *node = (struct node *)malloc(sizeof(struct node));

  node->data = data;

  node->left = NULL;

  node->right = NULL;

  return (node);

}


// Traverse Preorder

void traversePreOrder(struct node *temp) {

  if (temp != NULL) {

    cout << " " << temp->data;

    traversePreOrder(temp->left);

    traversePreOrder(temp->right);

  }

}


// Traverse Inorder

void traverseInOrder(struct node *temp) {

  if (temp != NULL) {

    traverseInOrder(temp->left);

    cout << " " << temp->data;

    traverseInOrder(temp->right);

  }

}


// Traverse Postorder

void traversePostOrder(struct node *temp) {

  if (temp != NULL) {

    traversePostOrder(temp->left);

    traversePostOrder(temp->right);

    cout << " " << temp->data;

  }

}


int main() {

  struct node *root = newNode(1);

  root->left = newNode(2);

  root->right = newNode(3);

  root->left->left = newNode(4);


  cout << "preorder traversal: ";

  traversePreOrder(root);

  cout << "\nInorder traversal: ";

  traverseInOrder(root);

  cout << "\nPostorder traversal: ";

  traversePostOrder(root);

}

 

 Ex50: Implementation of a Binary Search Tree

// C++ program to demonstrate delete operation in binary search tree

#include <iostream>

using namespace std;

struct node {

int key;

struct node *left;

struct node *right;

};

// A utility function to create a new BST node

struct node* newNode(int item)

{

struct node* temp = (struct node*)malloc(sizeof(struct node));

temp->key = item;

temp->left = temp->right = NULL;

return temp;

}


// A utility function to do in-order traversal of BST

void inorder(struct node* root)

{

if (root != NULL) {

inorder(root->left);

cout << root->key <<" ";

inorder(root->right);

}

}


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

struct node* insert(struct node* node, int key)

{

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

if (node == NULL)

return newNode(key);

/* Otherwise, recur down the tree */

if (key < node->key)

node->left = insert(node->left, key);

else

node->right = insert(node->right, key);


/* return the (unchanged) node pointer */

return node;

}

/* Given a non-empty binary search tree, return the node with minimum key value found in that tree. Note that the entire tree does not need to be searched. */

struct node* minValueNode(struct node* node)

{

struct node* current = node;

/* loop down to find the leftmost leaf */

while ((current && current->left) != NULL)

current = current->left;

return current;

}


/* Given a binary search tree and a key, this function deletes the key and returns the new root */

struct node* deleteNode(struct node* root, int key)

{

// base case

if (root == NULL)

return root;

// If the key to be deleted is smaller than the root's  key, then it lies in left subtree

if (key < root->key)

root->left = deleteNode(root->left, key);

// If the key to be deleted is greater than the root's key, then it lies in right subtree

else if (key > root->key)

root->right = deleteNode(root->right, key);


// if key is same as root's key, then This is the node to be deleted

else {

// node has no child

if (root->left == NULL and root->right == NULL)

return NULL;


// node with only one child or no child

else if (root->left == NULL) {

struct node* temp = root->right;

free(root);

return temp;

}

else if (root->right == NULL) {

struct node* temp = root->left;

free(root);

return temp;

}


// node with two children: Get the in-order successor

// (smallest in the right subtree)

struct node* temp = minValueNode(root->right);


// Copy the inorder successor's content to this node

root->key = temp->key;


// Delete the inorder successor

root->right = deleteNode(root->right, temp->key);

}

return root;

}


// Driver Code

int main()

{

/* Let us create following BST

                  50

/ \

           30         70

           / \          / \

20 40   60 80 */

struct node* root = NULL;

root = insert(root, 50);

root = insert(root, 30);

root = insert(root, 20);

root = insert(root, 40);

root = insert(root, 70);

root = insert(root, 60);

root = insert(root, 80);


cout << "Inorder traversal of the given tree \n";

inorder(root);


cout << "\nDelete 20\n";

root = deleteNode(root, 20);

cout << "Inorder traversal of the modified tree \n";

inorder(root);


cout << "\nDelete 30\n";

root = deleteNode(root, 30);

cout << "Inorder traversal of the modified tree \n";

inorder(root);


cout << "\nDelete 50\n";

root = deleteNode(root, 50);

cout << "Inorder traversal of the modified tree \n";

inorder(root);


return 0;

}


2-3 Trees | (Search, Insert and Deletion)

·       Difficulty Level : Medium

·       Last Updated : 05 Sep, 2022

·  Read

·  Discuss

·  Courses

·  Practice

·  Video

In  binary search trees we have seen the average-case time for operations like search/insert/delete is O(log N) and the worst-case time is O(N) where N is the number of nodes in the tree.

Like other Trees include AVL trees, Red Black Tree, B tree, 2-3 Tree is also a height balanced tree.

The time complexity of search/insert/delete is O(log N) .

A 2-3 tree is a B-tree of order 3.

Properties of 2-3 tree:

·       Nodes with two children are called 2-nodes. The 2-nodes have one data value and two children

·       Nodes with three children are called 3-nodes. The 3-nodes have two data values and three children.

·       Data is stored in sorted order.

·       It is a balanced tree.

·       All the leaf nodes are at same level.

·       Each node can either be leaf, 2 node, or 3 node.

·       Always insertion is done at leaf.

Search: To search a key K in given 2-3 tree T, we follow the following procedure: 

Base cases: 

1.    If T is empty, return False (key cannot be found in the tree).

2.    If current node contains data value which is equal to K, return True.

3.    If we reach the leaf-node and it doesn’t contain the required key value K, return False.

Recursive Calls: 

1.    If K < currentNode.leftVal, we explore the left subtree of the current node.

2.    Else if currentNode.leftVal < K < currentNode.rightVal, we explore the middle subtree of the current node.

3.    Else if K > currentNode.rightVal, we explore the right subtree of the current node.

Consider the following example:

https://media.geeksforgeeks.org/wp-content/uploads/Search_2-3Tree_img1.png

https://media.geeksforgeeks.org/wp-content/uploads/Search_2-3Tree_img2.png

https://media.geeksforgeeks.org/wp-content/uploads/Search_2-3Tree_img3.png

https://media.geeksforgeeks.org/wp-content/uploads/Search_2-3Tree_img4.png

Insertion: There are 3 possible cases in insertion which have been discussed below: 

·       Case 1: Insert in a node with only one data element 

https://media.geeksforgeeks.org/wp-content/uploads/Insertion_2-3Tree_img1.png

·       Case 2: Insert in a node with two data elements whose parent contains only one data element. 
 

https://media.geeksforgeeks.org/wp-content/uploads/Insert_2-3Tree_img2.png

https://media.geeksforgeeks.org/wp-content/uploads/Insert_2-3Tree_img3.png

https://media.geeksforgeeks.org/wp-content/uploads/Insert_2-3Tree_img4.png

·       Case 3: Insert in a node with two data elements whose parent also contains two data elements.  

https://media.geeksforgeeks.org/wp-content/uploads/Insert_2-3Tree_img5.png

https://media.geeksforgeeks.org/wp-content/uploads/Insert_2-3Tree_img6.png

https://media.geeksforgeeks.org/wp-content/uploads/Insert_2-3Tree_img7.png

https://media.geeksforgeeks.org/wp-content/uploads/Insert_2-3Tree_img8.png

In Deletion Process for a specific value:

·       To delete a value, it is replaced by its in-order successor and then removed.

·       If a node is left with less than one data value then two nodes must be merged together.

·       If a node becomes empty after deleting a value, it is then merged with another node.

To Understand the deletion process-

Consider the 2-3 tree given below

https://media.geeksforgeeks.org/wp-content/uploads/20220131222503/GFG-300x78.jpg

Given 2-3 Tree

delete the following values from it: 69,72, 99, 81.

To delete 69, swap it with its in-order successor, that is, 72. 69 now comes in the leaf node. Remove the value 69 from the leaf node.

https://media.geeksforgeeks.org/wp-content/uploads/20220131223404/GFG1-300x81.jpg

After deletion 69

To delete 72, 72 is an internal node. To delete this value swap 72 with its in-order successor 81 so that 72 now becomes a leaf node. Remove the value 72 from the leaf node.

https://media.geeksforgeeks.org/wp-content/uploads/20220131223809/GFG2-300x78.jpg

After deletion 72

Now there is a leaf node that has less than 1 data value thereby violating the property of a 2-3 tree. So the node must be merged.

To merge the node, pull down the lowest data value in the parent’s node and merge it with its left sibling.

https://media.geeksforgeeks.org/wp-content/uploads/20220131224412/GFG3-300x82.jpg

Rebalancing to Satisfy 23 Tree property

To delete 99, 99 is present in a leaf node, so the data value can be easily removed.

https://media.geeksforgeeks.org/wp-content/uploads/20220131224659/GFG4-300x84.jpg

After deletion 99

Now there is a leaf node that has less than 1 data value, thereby violating the property of a 2-3 tree.

So the node must be merged. To merge the node, pull down the lowest data value in the parent’s node and merge it with its left sibling.

https://media.geeksforgeeks.org/wp-content/uploads/20220131225019/GFG5-300x82.jpg

Rebalancing to Satisfy 2-3 Tree Property

To delete 81, 81 is an internal node. To delete this value swap 81 with its in-order successor 90 so that 81 now becomes a leaf node. Remove the value 81 from the leaf node.

https://media.geeksforgeeks.org/wp-content/uploads/20220131225536/GFG6-300x88.jpg

After deletion 81

Now there is a leaf node that has less than 1 data value, thereby violating the property of a 2-3 tree. So the node must be merged. To merge the node, pull down the lowest data value in the parent’s node and merge it with its left sibling.

https://media.geeksforgeeks.org/wp-content/uploads/20220131225856/GFG7-300x96.jpg

Rebalancing to Satisfy 2-3 Tree property

As internal node cannot be empty. So now pull down the lowest data value from the parent’s node and merge the empty node with its left sibling

https://media.geeksforgeeks.org/wp-content/uploads/20220131230613/GFG-300x82.jpg

Rebalancing to Satisfy 2-3 Tree Property

NOTE: In a 2-3 tree, each interior node has either two or three children. This means that a 2-3 tree is not a binary tree.

 

Ref:  https://www.geeksforgeeks.org/introduction-to-tree-data-structure-and-algorithm-tutorials/

https://www.geeksforgeeks.org/introduction-to-binary-tree-data-structure-and-algorithm-tutorials/?ref=rp

https://www.geeksforgeeks.org/deletion-in-binary-search-tree/

https://www.geeksforgeeks.org/2-3-trees-search-and-insert/