Data Structures (Tree)
Data Structures (Tree)
TOPICS COVERED-
▪ Introduction to Trees
▪ Binary Tree Traversals
▪ Level Order Traversal of a Binary Tree
▪ Insertion in a Binary Tree
▪ Deletion in a Binary Tree
▪ Finding LCA in Binary Tree
▪ Diameter of a Binary Tree
▪ Left, Right, Top and Bottom View of a Binary Tree
▪ Threaded Binary Tree Sample
▪ Problems on Trees
Introduction to Trees-
A Tree is a non-linear data structure where each node is connected to a
number of nodes with the help of pointers or references.
• Root: The root of a tree is the first node of the tree. In the above image, the
root node is the node 30.
• Edge: An edge is a link connecting any two nodes in the tree. For example, in
the above image there is an edge between node 11 and 6.
• Siblings: The children nodes of same parent are called siblings. That is, the
nodes with same parent are called siblings. In the above tree, nodes 5, 11,
and 63 are siblings.
• Leaf Node: A node is said to be the leaf node if it has no children. In the
above tree, node 15 is one of the leaf nodes.
• Height of a Tree: Height of a tree is defined as the total number of levels in
the tree or the length of the path from the root node to the node present at
the last level. The above tree is of height 2.
Binary Tree
A Tree is said to be a Binary Tree if all of its nodes have atmost 2 children.
That is, all of its node can have either no child, 1 child, or 2 child nodes.
1. The maximum number of nodes at level 'l' of a binary tree is (2l - 1). Level of
root is 1.
4. A Binary Tree with L leaves has at least (Log2L + 1) levels. A Binary tree
has maximum number of leaves (and minimum number of levels) when all
levels are fully filled. Let all leaves be at level l, then below is true for
number of leaves L.
5. In a Binary tree in which every node has 0 or 2 children, the number of leaf
nodes is always one more than the nodes with two children.
L = T + 1
Where L = Number of leaf nodes
T = Number of internal nodes with two children
Types of Binary Trees: Based on the structure and number of parents and children
nodes, a Binary Tree is classified into the following common types:
• Full Binary Tree: A Binary Tree is full if every node has either 0 or 2 children.
The following are examples of a full binary tree. We can also say that a full
binary tree is a binary tree in which all nodes except leave nodes have two
children.
• Complete Binary Tree: A Binary Tree is a complete Binary Tree if all levels
are completely filled except possibly the last level and the last level has all
keys as left as possible
• Perfect Binary Tree: A Binary tree is a Perfect Binary Tree when all internal
nodes have two children and all the leave nodes are at the same level.
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)
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)
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.
C++ :-
// C++ program for different tree traversals /* now recur on right child */
/* A binary tree node has data, pointer to left child // Function to print the PreOrder traversal
{ {
}; printPreorder(node->right);
{ {
return; // 1
printPostorder(node->left); // 2 3
printPostorder(node->right); // 4 5
// now deal with the node struct Node *root = new Node(1);
void printInorder(struct Node* node) cout << "Preorder traversal of binary tree is \n";
{ printPreorder(root);
return; printInorder(root);
/* first recur on left child */ cout << "\nPostorder traversal of binary tree is \n";
printInorder(node->left); printPostorder(root);
Java :-
// Java program for different tree traversals if (node == null)
{ }
} {
} if (node == null)
BinaryTree() printPreorder(node.left);
} }
printPostorder(node.left); {
Output:
Preorder traversal of binary tree is
1 2 4 5 3
4 2 5 1 3
4 5 2 3 1
In the Level Order Traversal, the binary tree is traversed level-wise starting from
the first to last level sequentially.
The Level Order Traversal of the above Binary Tree will be: 10 20 30 40 50 60 70
80.
o Pop the top node from queue and print the node.
o Enqueue node's children (first left then right children) to q
o Repeat the process until queue is not empty.
Implementation:
C++ :-
// C++ program to print level order traversal {
{ if (node->left != NULL)
}; if (node->right != NULL)
{ }
} // 1
{ // 4 5
// Enqueue Root and initialize height cout << "Level Order traversal of binary
tree is \n";
q.push(root);
printLevelOrder(root);
return 0; }
while (q.empty() == false)
java :-
// Iterative Queue based Java program to do if (tempNode.left != null) {
import java.util.Queue; }
int data; }
right = null; {
} // 1
class BinaryTree { // 2 3
Node root; // /\
level order using array for implementing queue */ BinaryTree tree_level = new BinaryTree();
Output:
1 2 3 4 5
The idea is to do iterative level order traversal of the given tree using a queue. If we find a
node whose left child is empty, we make new key as the left child of the node. Else if we
find a node whose right child is empty, we make new key as the right child of that node.
We keep traversing the tree until we find a node whose either left or right child is empty.
C++ :-
// C++ program to insert element in binary tree };
struct Node* temp = new Node; void insert(struct Node* temp, int key)
temp->key = key; {
// an empty place. // / \
while (!q.empty()) { // 11 9
q.pop(); // 7 8
break; inorder(root);
{ }
Java :-
// Java program to insert element in binary tree static Node root;
// constructor inorder(temp.left);
left = null; }
} // Binary Tree
q.add(temp); // 10
// an empty place. // 11 9
while (!q.isEmpty()) { // / \
temp = q.peek(); // 7 8
Output:
Examples:
Output :
Output :
While performing the delete operation on binary trees, there arise a few cases:
1. The node to be deleted is a leaf node. That is it does not have any children.
2. The node to be deleted is a internal node. That is it have left or right child.
3. The node to be deleted is the root node.
In the first case 1, since the node to be deleted is a leaf node, we can simply delete
the node without any overheads. But in the next 2 cases, we will have to take care
of the children of the node to be deleted.
In order to handle all of the cases, one way to delete a node is to:
1. Starting at the root, find the deepest and rightmost node in binary tree and
node which we want to delete.
2. Replace the deepest rightmost node’s data with the node to be deleted.
3. Then delete the deepest rightmost node.
C++ :-
// C++ program to delete element in binary tree };
{ return;
struct Node* left, *right; cout << temp->key << " ";
}; inorder(temp->right);
{ q.pop();
if (temp->right) if (temp->left)
{ q.push(temp->left);
{ q.push(temp->right);
temp->right = NULL; }
} key_node->key = x;
else }
} int main()
if (temp->left) {
if (temp->left == d_node) // 10
{ // / \
temp->left=NULL; // 11 9
delete(d_node); // / \ /\
return; // 7 12 15 8
} root->left->right = newNode(12);
} root->right = newNode(9);
} root->right->left = newNode(15);
void deletion(struct Node* root, int key) cout << "Inorder traversal before deletion : ";
{ inorder(root);
struct Node *key_node = NULL; cout << "Inorder traversal after deletion : ";
while (!q.empty()) }
Java-
// Java program to delete element in binary tree temp = q.peek();
// constructor return;
Node(int key){ }
right = null; }
} if (temp.left!=null)
} {
{ }
return; q.add(temp.left);
inorder(temp.left); }
System.out.print(temp.key+" "); }
inorder(temp.right); }
// Function to delete the given deepest node static void deletion(Node root, int key)
{ q.add(root);
// Do level order traversal until last node // Do level order traversal to find deepest
{ {
temp = q.peek(); // 11 9
q.remove(); // / \ /\
if (temp.key == key) // 7 12 15 8
Output:
Below image represents a tree and LCA of different pair of nodes (N1, N2) in it:
Finding LCA
Method 1: The simplest method of finding LCA of two nodes in a Binary Tree is to
observe that the LCA of the given nodes will be the last common node in the paths
from the root node to the given nodes.
Algorithm:
1. Find the path from the root node to node n1 and store it in a vector or array.
2. Find the path from the root node to node n2 and store it in another vector or
array.
3. Traverse both paths untill the values in arrays are same. Return the common
element just before the mismatch.
Implementation:
C++ :-
// C++ Program for Lowest Common Ancestor return true;
}; }
// Utility function creates a new binary tree // Function to return LCA if node n1, n2 are
// node with given key // present in the given binary tree, otherwise
// Function to find the path from root node to if (!findPath(root, path1, n1) || !findPath(root, path2,
n2))
// given root of the tree, Stores the path in a
return -1;
// vector path[], returns true if path exists
// Compare the paths to get the first
// otherwise false
// different value
bool findPath(Node* root, vector<int>& path, int k)
int i;
{
for (i = 0; i < path1.size() && i < path2.size(); i++)
// base case
if (path1[i] != path2[i])
if (root == NULL)
break;
return false;
return path1[i - 1];
// Store this node in path vector.
}
// The node will be removed if
// Driver Code
// not in path from root to k
int main()
path.push_back(root->key);
{
root->right->right = newNode(7); }
Java :-
// Java Program for Lowest Common Ancestor System.out.println((path2.size() > 0) ?
import java.util.List; }
Node(int value) }
data = value; }
left = right = null; // Finds the path from root node to given
// Finds the path from root node to given root of the // base case
tree
if (root == null) {
int findLCA(int n1, int n2)
return false;
{
}
path1.clear();
// Store this node. The node will be removed if
path2.clear();
// not in path from root to n.
return findLCAInternal(root, n1, n2);
path.add(root.data);
}
if (root.data == n) {
private int findLCAInternal(Node root, int n1, int n2)
return true;
{
}
if (!findPath(root, n1, path1) || !findPath(root, n2,
path2)) { if (root.left != null && findPath(root.left, n, path)) {
Output:
LCA(4, 5) = 2
LCA(4, 6) = 1
LCA(3, 4) = 1
LCA(2, 4) = 2
Analysis: The time complexity of the above solution is O(N) where N is the number
of nodes in the given Tree and the above solution also takes O(N) extra space.
Method 2: The method 1 finds LCA in O(N) time but requires three tree traversals
plus extra spaces for path arrays. If we assume that the keys are present in Binary
Tree, we can find LCA using single traversal of Binary Tree and without extra
storage for path arrays.
The idea is to traverse the tree starting from the root node. If any of the given keys
(n1 and n2) matches with root, then root is LCA (assuming that both keys are
present). If root doesn't match with any of the keys, we recur for left and right
subtrees. The node which has one key present in its left subtree and the other key
present in the right subtree is the LCA. If both keys lie in left subtree, then left
subtree has LCA also, otherwise, LCA lies in the right subtree.
C++ :-
// C++ Program to find LCA of n1 and n2 using return root;
// one traversal of Binary Tree // Look for keys in left and right subtrees
};
{ }
return temp; {
struct Node* findLCA(struct Node* root, int n1, int n2) root->left->right = newNode(5);
{ root->right->left = newNode(6);
if (root == NULL)
// If either n1 or n2 matches with root's key, report cout << "nLCA(3, 4) = " << findLCA(root, 3, 4)->key;
// the presence by returning root (Note that if a key is cout << "nLCA(2, 4) = " << findLCA(root, 2, 4)->key;
Java :-
// Java implementation to find lowest common if (node.data == n1 || node.data == n2)
ancestor of
return node;
// n1 and n2 using one traversal of binary tree
// Look for keys in left and right subtrees
// Class containing left and right child of current
Node left_lca = findLCA(node.left, n1, n2);
// node and key value
Node right_lca = findLCA(node.right, n1, n2);
class Node {
// If both of the above calls return Non-NULL, then
int data; one key
// This function returns pointer to LCA of two given tree.root.left.left = new Node(4);
// values n1 and n2. This function assumes that n1 tree.root.left.right = new Node(5);
and
tree.root.right.left = new Node(6);
// n2 are present in Binary Tree
tree.root.right.right = new Node(7);
Node findLCA(Node node, int n1, int n2)
System.out.println("LCA(4, 5) = " +
{ tree.findLCA(4, 5).data);
// Base case System.out.println("LCA(4, 6) = " +
tree.findLCA(4, 6).data);
if (node == null)
System.out.println("LCA(3, 4) = " +
return null; tree.findLCA(3, 4).data);
// If either n1 or n2 matches with root's key, report System.out.println("LCA(2, 4) = " +
tree.findLCA(2, 4).data);
// the presence by returning root (Note that if a
key is }
// ancestor of other, then the ancestor key }
becomes LCA
Output:
LCA(4, 5) = 2nLCA(4, 6) = 1nLCA(3, 4) = 1nLCA(2, 4) = 2
The longest path between leaves that goes through a particular node say, nd can
be calculated as:
Implementation:
// C++ program to calculate Diameter of a Binary Tree number f nodes along the longest path from the
root node
#include <bits/stdc++.h>
down to the farthest leaf node.*/
using namespace std;
int height(struct node* node)
// Binary Tree Node
{
struct node
/* base case tree is empty */
{
if(node == NULL)
int data;
return 0;
struct node* left, *right;
/* If tree is not empty then height = 1 + max of left
};
height and right heights */
// Function to create a new node of tree
return 1 + max(height(node->left), height(node-
// and returns pointer >right));
struct node* newNode(int data); }
// Function to Compute height of a tree /* Helper function that allocates a new node with the
int height(struct node* node); given data and NULL left and right pointers. */
// Function to get diameter of a binary tree struct node* newNode(int data)
int diameter(struct node * tree) {
{ struct node* node = (struct node*)
/* base case where tree is empty */ malloc(sizeof(struct node));
if (tree == NULL) node->data = data;
} 4 5
Java :-
// Recursive optimized Java program to find the 3) Height of left subtree + height of right subtree
diameter of a + 1 */
{ int diameter()
int data; {
return 0; {
/* get the height of left and right sub trees */ /* creating a binary tree and entering the nodes */
int lheight = height(root.left); BinaryTree tree = new BinaryTree();
int rheight = height(root.right); tree.root = new Node(1);
/* get the diameter of left and right subtrees */ tree.root.left = new Node(2);
int ldiameter = diameter(root.left); tree.root.right = new Node(3);
int rdiameter = diameter(root.right); tree.root.left.left = new Node(4);
/* Return max of following three tree.root.left.right = new Node(5);
1) Diameter of left subtree System.out.println("The diameter of given binary
tree is : "
2) Diameter of right subtree
+ tree.diameter()); }
Output:
Time Complexity: O(N2), where N is the number of nodes in the binary tree.
Example:
• Left View: A simple solution is to notice that the nodes appearing in the left
view of the binary tree are the first nodes at every level. So, the idea is to do
a level order traversal of the binary tree using a marker to identify levels and
print the first node at every level.
// Function to print the left view of the binary tree // If left child is present
{ if (temp->left)
return; if (temp->right)
// Delimiter q.pop();
while (!q.empty()) { }
// of each level }
• Right View: Printing Right View of the Binary Tree is similar to the above
approach of printing the Left view of the tree. The idea is to print the last
node present at every level. So, perform a level order traversal using a
delimeter to identify levels and print the last node present at every level.
• Top View: Top view of a binary tree is the set of nodes visible when the tree
is viewed from the top. A node x is there in output if x is the topmost node at
its horizontal distance. Horizontal distance of left child of a node x is equal to
the horizontal distance of x minus 1, and that of right child is the horizontal
distance of x plus 1.
So, the idea is to do a level order traversal of the tree and calculate the
horizontal distance of every node from the root node and print those nodes
Hashing can be used to keep a check on whether any node with a particular
horizontal distance is encountered yet or not.
// Structure of binary tree print "The top view of the tree is : ";
{ q.push(root->left);
if(root==NULL) }
map<int,int> m; if(root->right)
int hd=0; {
q.push(root); }
q.pop();
root=q.front(); {
} cout<<i->second<<" ";
for(auto i=m.begin();i!=m.end();i++)
• Bottom View: The Bottom View of a binary tree can be printed using the
similar approach as that of printing the Top View. A node x is there in output
if x is the bottom most instead of the top most node at its horizontal distance.
The process of printing the bottom view is almost the same as that of top
view with a little modification that while storing the node's data along with a
particular horizontal distance in the map, keep updating the node's data in
the map for a particular horizontal distance so that the map contains the last
node appearing with a particular horizontal distance instead of first.
Note: The threads are also useful for fast accessing ancestors of a node.
Following diagram shows an example Single Threaded Binary Tree. The dotted
lines represent threads.
{ {
data = item;
}}
Since the right pointer in a Threaded Binary Tree is used for two purposes, the
boolean variable rightThread is used to indicate whether the right pointer points to
a right child or an inorder successor. Similarly, we can add leftThread for a double
threaded binary tree.
1. Start from the root node, go to the leftmost node and print the node.
2. Check if there is a thread towards the right for the current node.
o If Yes, then follow the thread to the node and print the data of node
linked with this thread.
o Otherwise follow the link to the right subtree, find the leftmost node
in the right subtree and print the leftmost node.
if (N == NULL) {
if (cur->rightThread)
// right subtree
{ }
Problems on Trees-
A node x is there in output if x is the topmost node at its horizontal distance. The horizontal
distance of left child of a node x is equal to a horizontal distance of x minus 1, and that of a
right child is the horizontal distance of x plus 1.
}
q.pop()
root=q.front()
}
for(it=m.begin();it!=m.end();it++)
{
print(it->second)
}
}
Pseudo Code
int diameter(struct node *root, int* height)
{
/* lh --> Height of left subtree
rh --> Height of right subtree */
int lh = 0, rh = 0
if(root == NULL)
{
*height = 0
return 0 /* diameter is also 0 */
}
Solution - The idea is to recursively call for left and right subtrees of a given node. On each
recursive call swap the pointers of the children nodes.
Pseudo Code
mirror(node->right)
HIMANSHU KUMAR(LINKEDIN)
https://www.linkedin.com/in/himanshukumarmahuri
CREDITS- INTERNET
DISCLOSURE- ALL THE DATA AND IMAGES ARE TAKEN FROM GOOGLE AND INTERNET.