Tree Data Structure Notes
Tree Data Structure Notes
Tree Data Structure Notes
A tree is a kind of data structure that is used to represent the data in hierarchical form. It can
be defined as a collection of objects or entities called as nodes that are linked together to
simulate a hierarchy. Tree is a non-linear data structure as the data in a tree is not stored linearly
or sequentially.
A tree is also one of the data structures that represent hierarchical data. Suppose we want to
show the employees and their positions in the hierarchical form then it can be represented as
shown below:
The above tree shows the organization hierarchy of some company. In the above
structure, john is the CEO of the company, and John has two direct reports named
as Steve and Rohan. Steve has three direct reports named Lee, Bob, Ella where Steve is a
manager. Bob has two direct reports named Sal and Emma. Emma has two direct reports
named Tom and Raj. Tom has one direct report named Bill. This particular logical structure is
known as a Tree. Its structure is similar to the real tree, so it is named a Tree. In this structure,
the root is at the top, and its branches are moving in a downward direction. Therefore, we can
say that the Tree data structure is an efficient way of storing the data in a hierarchical way.Skip
10s
In the above structure, each node is labeled with some number. Each arrow shown in the above
figure is known as a link/edge between the two nodes.
o Root: The root node is the topmost node in the tree hierarchy. In other words, the root
node is the one that doesn't have any parent. In the above structure, node numbered 1
o Recursive data structure: The tree is also known as a recursive data structure. A tree
can be defined as recursively because the distinguished node in a tree data structure is
known as a root node. The root node of the tree contains a link to all the roots of its
subtrees. The left subtree is shown in the yellow color in the below figure, and the right
subtree is shown in the red color. The left subtree can be further split into subtrees
shown in three different colors. Recursion means reducing something in a self-
similar manner. So, this recursive property of the tree data structure is implemented
o Number of edges: If there are n nodes, then there would n-1 edges. Each arrow in the
structure represents the link or path. Each node, except the root node, will have atleast
one incoming link known as an edge. There would be one link for the parent-child
relationship.
o Depth of node x: The depth of node x can be defined as the length of the path from the
root to the node x. One edge contributes one-unit length in the path. So, the depth of
node x can also be defined as the number of edges between the root node and the
node x. The root node has 0 depth.
o Height of node x: The height of node x can be defined as the longest path from the
node x to the leaf node.
Based on the properties of the Tree data structure, trees are classified into various categories.
Implementation of Tree
The tree data structure can be created by creating the nodes dynamically with the help of the
pointers. The tree in the memory can be represented as shown below:
1. struct node
2. {
3. int data;
4. struct node *left;
5. struct node *right;
6. }
The above structure can only be defined for the binary trees because the binary tree can have
utmost two children, and generic trees can have more than two children. The structure of the
node for generic trees would be different as compared to the binary tree.
o General tree: The general tree is one of the types of tree data structure. In the general
tree, a node can have either 0 or maximum n number of nodes. There is no restriction
imposed on the degree of the node (the number of nodes that a node can contain). The
topmost node in a general tree is known as a root node. The children of the parent node
are known as subtrees.
A node can be created with the help of a user-defined data type known as struct, as shown
below:
1. struct node
2. {
3. int data;
4. struct node *left;
5. struct node *right;
6. }
The above is the node structure with three fields: data field, the second field is the left pointer
of the node type, and the third field is the right pointer of the node type.
o AVL tree
It is one of the types of the binary tree, or we can say that it is a variant of the binary search
tree. AVL tree satisfies the property of the binary tree as well as of the binary search tree. It
is a self-balancing binary search tree that was invented by Adelson Velsky Lindas. Here, self-
balancing means that balancing the heights of left subtree and right subtree. This balancing is
measured in terms of the balancing factor.
We can consider a tree as an AVL tree if the tree obeys the binary search tree as well as a
balancing factor. The balancing factor can be defined as the difference between the height of
the left subtree and the height of the right subtree. The balancing factor's value must be either
0, -1, or 1; therefore, each node in the AVL tree should have the value of the balancing factor
either as 0, -1, or 1.
The above tree is a binary tree because each node contains the utmost two children. The logical
representation of the above tree is given below:
As we know that,
n = 2h+1 -1
n+1 = 2h+1
log2(n+1) = log2(2h+1)
log2(n+1) = h+1
h = log2(n+1) - 1
As we know that,
n = h+1
h= n-1
In the above tree, we can observe that each node is either containing zero or two children;
therefore, it is a Full Binary tree.
o The number of leaf nodes is equal to the number of internal nodes plus 1. In the above
example, the number of internal nodes is 2; therefore, the number of leaf nodes is equal
to 3.
n= 2*h - 1
n+1 = 2*h
h = n+1/2
The above tree is a complete binary tree because all the nodes are completely filled, and all the
nodes in the last level are added at the left first.
The below tree is not a perfect binary tree because all the leaf nodes are not at the same level.
The above tree is a degenerate binary tree because all the nodes have only one child. It is also
known as a right-skewed tree as all the nodes have a right child only.
The above tree is a balanced binary tree because the difference between the left subtree and
right subtree is zero.
A Binary tree is implemented with the help of pointers. The first node in the tree is represented
by the root pointer. Each node in the tree consists of three parts, i.e., data, left pointer and right
pointer. To create a binary tree, we first need to create the node. We will create the node of
user-defined as shown below:
1. struct node
2. {
3. int data,
4. struct node *left, *right;
5. }
In the above structure, data is the value, left pointer contains the address of the left node,
and right pointer contains the address of the right node.
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.
Uses of Postorder:
Postorder traversal is used to delete the tree. Postorder traversal is also useful to get the
postfix expression of an expression tree
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.
Now, the creation of binary search tree is completed. After that, let's move towards the
operations that can be performed on Binary search tree.
We can perform insert, delete and search operations on the binary search tree.
Let's understand how a search is performed on a binary search tree.
Step2:
Step3:
Insertion O(n)
Deletion O(n)
Search O(n)
// A tree node
struct Node
{
int data;
struct Node *left, *right;
};
// Driver code
int main(void)
{
struct Node* NewRoot = NULL;
struct Node* root = newNode(2);
root->left = newNode(7);
root->right = newNode(5);
root->left->right = newNode(6);
root->left->right->left = newNode(1);
root->left->right->right = newNode(11);
root->right->right = newNode(9);
root->right->right->left = newNode(4);
// Function call
printf("Maximum element is %d \n", findMax(root));
return 0;
}
Output
Maximum element is 11
Complexity Analysis:
Time Complexity: O(N).
In the recursive function calls, every node of the tree is processed once and hence the
complexity due to the function is O(N) if there are total N nodes in the tree. Therefore, the
time complexity is O(N).
Space Complexity: O(N).
Recursive call is happening. The every node is processed once and considering the stack
space, the space complexity will be O(N).
Find maximum and minimum element in binary tree without using recursion or stack
or queue
Given a binary tree. The task is to find out the maximum and minimum element in a binary
tree without using recursion or stack or queue i.e, space complexity should be O(1).
Examples:
Input :
12
/ \
13 10
/ \
14 15
/ \ / \
21 24 22 23
Input :
12
/ \
19 82
/ / \
41 15 95
\ / / \
2 21 7 16
Approach :
1. Initialize current as root
2. Take to variable max and min
3. While current is not NULL
• If the current does not have left child
• Update variable max and min with current’s data if required
• Go to the right, i.e., current = current->right
• Else
• Make current as the right child of the rightmost
node in current’s left subtree
• Go to this left child, i.e., current = current->left
Method 1: Using Inorder Traversal (O(n) time and O(h) auxiliary space)
The Inorder Traversal of a BST traverses the nodes in increasing order. So, the idea is to
traverse the tree in Inorder. While traversing, keep track of the count of the nodes visited. If
the count becomes k, print the node.
Time complexity: O(h) where h is the height of the tree.
Auxiliary Space: O(h)
We can optimize space using Morris Traversal. In Morris traversal, we first create links to
Inorder successor and print the data using these links, and finally revert the changes to restore
original tree. In this case,
Time Complexity: O(n) where n is the size of BST
Auxiliary Space: O(1)
Method 2: Using Any Tree Traversal (pre-in-post) than return kth smallest easily.
Approach:
Here we use pre order traversal than sort it and return the kth smallest element.
Algorithm:
Here we have tree we will take preorder of it as :
Method 3: Augmented Tree Data Structure (O(h) Time Complexity and O(h) auxiliary
space)
The idea is to maintain the rank of each node. We can keep track of elements in the left
subtree of every node while building the tree. Since we need the K-th smallest element, we
can maintain the number of elements of the left subtree in every node.
Assume that the root is having ‘lCount’ nodes in its left subtree. If K = lCount + 1, root is K-
th node. If K < lCount + 1, we will continue our search (recursion) for the Kth smallest
element in the left subtree of root. If K > lCount + 1, we continue our search in the right
subtree for the (K – lCount – 1)-th smallest element. Note that we need the count of elements
in the left subtree only.
Time complexity: O(h) where h is the height of the tree.
Auxiliary Space: O(h)
Method 4: Iterative approach using Stack: The basic idea behind the Iterative Approach
using Stack to find the kth smallest element in a Binary Search Tree (BST) is to traverse the
tree in an inorder fashion using a stack until we find the kth smallest element. Follow the
steps to implement the above idea:
1. Create an empty stack and set the current node to the root of the BST.
2. Push all the left subtree nodes of the current node onto the stack until the current
node is NULL.
3. Pop the top node from the stack and check if it is the k-th element. If it is, return
its value.
4. Decrement the value of k by 1.
5. Set the current node to the right child of the popped node.
6. Go to step 2 if the stack is not empty or k is not equal to 0.