Trees
Trees
The first node of the tree is called the root. If this root node is connected by another node,
the root is then a parent node and the connected node is a child.
All Tree nodes are connected by links called edges. It’s an important part of trees, because it’s
manages the relationship between nodes.
Leaves are the last nodes on a tree. They are nodes without children. Like real trees, we have
the root, branches, and finally the leaves.
Properties of Tree: Every tree has a specific root node. Each tree node can be crossed by a
root node. It is called root, as the tree was the only root. Every child has only one parent, but
the parent can have many children.
1. General Tree
If no constraint is placed on the hierarchy of the tree, a tree is called a general tree. Every
node may have infinite numbers of children in General Tree. The tree is the super-set of all
other trees.
2. Binary Tree
The binary tree is the kind of tree in which most two children can be found for each parent.
The kids are known as the left kid and right kid. This is more popular than most other trees.
When certain constraints and characteristics are applied in a Binary tree, a number of others
such as AVL tree, BST (Binary Search Tree), RBT tree, etc. are also used. When we move
forward, we will explain all these styles in detail.
4. AVL Tree
AVL tree is a binary search tree self-balancing. On behalf of the inventors Adelson-Velshi
and Landis, the name AVL is given. This was the first tree that balanced dynamically. A
balancing factor is allocated for each node in the AVL tree, based on whether the tree is
balanced or not. The height of the node kids is at most 1. AVL vine. In the AVL tree, the
correct balance factor is 1, 0 and -1. If the tree has a new node, then it will be rotated to
ensure that the tree is balanced. It will then be rotated. Common operations such as viewing,
insertion, and removal take O(log n) time in the AVL tree. It is mostly applied when working
with Lookups operations.
5. Red-Black Tree
Another kind of auto-balancing tree is red-black. The red-black name is given because the
Red-black tree has either red or Black painted on each node according to the red-black tree’s
properties. It maintains the balance of the forest. Even though this tree is not a fully balanced
tree, the searching operation only takes O (log n) time. When the new nodes are added in
Red-Black Tree then nodes will be rotated again to maintain the Red-Black Tree’s properties.
6. N-ary Tree
The maximum number of children in this type of tree that can have a node is N. A binary tree
is a twoyear tree, as at most 2 children in every binary tree node. A complete N-ary tree is a
tree where kids of a node either are 0 or N.
Advantages of Tree
Now we will understand the Advantages of Tree:
• The trees are flexible. This allows subtrees to be relocated with minimal effort.
Tree represents the nodes connected by edges. We will discuss binary tree or binary search
tree specifically.
Binary Tree is a special datastructure used for data storage purposes. A binary tree has a
special condition that each node can have a maximum of two children. A binary tree has the
benefits of both an ordered array and a linked list as search is as quick as in a sorted array
and insertion or deletion operation are as fast as in linked list.
Important Terms
We're going to implement tree using node object and connecting them through references.
Tree Node
The code to write a tree node would be similar to what is given below. It has a data part and
references to its left and right child nodes.
struct node {
int data;
struct node *leftChild;
struct node *rightChild;
};
In a tree, all nodes share common construct.
Insert Operation
The very first insertion creates the tree. Afterwards, whenever an element is to be inserted,
first locate its proper location. Start searching from the root node, then if the data is less than
the key value, search for the empty location in the left subtree and insert the data. Otherwise,
search for the empty location in the right subtree and insert the data.
Algorithm
If root is NULL
then create root node
return
endwhile
insert data
end If
Implementation
while(1) {
parent = current;
Whenever an element is to be searched, start searching from the root node, then if the data is
less than the key value, search for the element in the left subtree. Otherwise, search for the
element in the right subtree. Follow the same algorithm for each node.
Algorithm
If root.data is equal to
search.data return root
else
while data not found
If data found
return node
endwhile
end
if
The implementation of this algorithm should look like this.
struct node* search(int data)
{ struct node *current = root;
printf("Visiting elements: ");
while(current->data != data)
{ if(current != NULL)
printf("%d ",current->data);
//not found
if(current == NULL) {
return NULL;
}
return current;
}
}
Traversal is a process to visit all the nodes of a tree and may print their values too. Because,
all nodes are connected via edges (links) we always start from the root (head) node. That is,
we cannot randomly access a node in a tree. There are three ways which we use to traverse a
tree –
• Pre-order Traversal
• In-order Traversal
• Post-order Traversal
Generally, we traverse a tree to search or locate a given item or key in the tree or to print all
the values it contains.
Why?
When we go deep to the leaf and backtrack, this is called DFS algorithm.
Now that we are familiar with this traversal algorithm, we will discuss types of DFS: pre-
order, in-order, and post-order.
Pre-order Traversal
if self.left_child:
self.left_child.pre_order()
if self.right_child:
self.right_child.pre_order()
In this traversal method, the root node is visited first, then the left subtree and finally the right
subtree.
We start from A, and following pre-order traversal, we first visit A itself and then move to
its left subtree B. B is also traversed pre-order. The process goes on until all the nodes are
visited. The output of pre-order traversal of this tree will be −
A→B→D→E→C→F→G
Algorithm
The result of the in-order algorithm for this tree example is 3–2–4–1–6–5–7.
The left first, the middle second, and the right last.
def in_order(self):
if self.left_child:
self.left_child.in_order()
print(self.value)
if self.right_child:
self.right_child.in_order()
1. Go to the left child and print it. This is if, and only if, it has a left child.
2. Print the node’s value
3. Go to the right child and print it. This is if, and only if, it has a right child.
In this traversal method, the left subtree is visited first, then the root and later the right sub-
tree. We should always remember that every node may represent a subtree itself.
If a binary tree is traversed in-order, the output will produce sorted key values in an
ascending order.
We start from A, and following in-order traversal, we move to its left subtree B. B is also
traversed inorder. The process goes on until all the nodes are visited. The output of inorder
traversal of this tree will be −
D→B→E→A→F→C→G
Algorithm
Post-order Traversal
Post-order
The result of the post order algorithm for this tree example is 3–
4–2–6–7–5–1.
The left first, the right second, and the middle last.
def post_order(self):
if self.left_child:
self.left_child.post_order()
if self.right_child:
self.right_child.post_order()
print(self.value)
1. Go to the left child and print it. This is if, and only if, it
has a left child.
2. Go to the right child and print it. This is if, and only if, it
has a right child.
3. Print the node’s value
In this traversal method, the root node is visited last, hence the name. First we traverse the left
subtree, then the right subtree and finally the root node.
We start from A, and following Post-order traversal, we first visit the left subtree B. B is also
traversed post-order. The process goes on until all the nodes are visited. The output of post-
order traversal of this tree will be −
D→E→B→F→G→C→A
Algorithm
A Binary Search Tree (BST) is a tree in which all the nodes follow the below-mentioned
properties − The left sub-tree of a node has a key less than or equal to its parent node's key.
The right sub-tree of a node has a key greater than to its parent node's key.
Thus, BST divides all its sub-trees into two segments; the left sub-tree and the right sub-tree
and can be defined as −
Representation
BST is a collection of nodes arranged in a way where they maintain BST properties. Each
node has a key and an associated value. While searching, the desired key is compared to the
keys in BST and if found, the associated value is retrieved.
Following is a pictorial representation of BST −
We observe that the root node key (27) has all less-valued keys on the left sub-tree and the
higher valued keys on the right sub-tree.
Basic Operations
Node
Define a node having some data, references to its left and right child nodes.
struct node {
Search Operation
Whenever an element is to be searched, start searching from the root node. Then if the data is
less than the key value, search for the element in the left subtree. Otherwise, search for the
element in the right subtree. Follow the same algorithm for each node.
while(current->data != data){
if(current != NULL) {
printf("%d ",current->data);
//not found
if(current == NULL){
return NULL;
}
return current;
Insert Operation
Whenever an element is to be inserted, first locate its proper location. Start searching from the
root node, then if the data is less than the key value, search for the empty location in the left
subtree and insert the data. Otherwise, search for the empty location in the right subtree and
insert the data.
Algorithm void insert(int data) { struct node *tempNode =
(struct node*) malloc(sizeof(struct node)); struct node
*current; struct node *parent;
if(current == NULL)
{ parent->leftChild =
tempNode;
return;
return;
}
}
What if the input to binary search tree comes in a sorted (ascending or descending) manner?
It will then look like this −
It is observed that BST's worst-case performance is closest to linear search algorithms, that
is Ο(n). In real-time data, we cannot predict data pattern and their frequencies. So, a need
arises to balance out the existing BST.
Named after their inventor Adelson, Velski & Landis, AVL trees are height balancing
binary search tree. AVL tree checks the height of the left and the right subtrees and assures
that the difference is not more than 1. This difference is called the Balance Factor.
Here we see that the first tree is balanced and the next two trees are not balanced −
In the second tree, the left subtree of C has height 2 and the right subtree has height 0, so the
difference is 2. In the third tree, the right subtree of A has height 2 and the left is missing, so
it is 0, and the difference is 2 again. AVL tree permits difference (balance factor) to be only
1.
BalanceFactor = height(left-sutree) − height(right-sutree)
If the difference in the height of left and right sub-trees is more than 1, the tree is balanced
using some rotation techniques.
AVL Rotations
To balance itself, an AVL tree may perform the following four kinds of rotations −
• Left rotation
• Right rotation
• Left-Right rotation
• Right-Left rotation
The first two rotations are single rotations and the next two rotations are double rotations. To
have an unbalanced tree, we at least need a tree of height 2. With this simple tree, let's
understand them one by one.
Left Rotation
If a tree becomes unbalanced, when a node is inserted into the right subtree of the right
subtree, then we perform a single left rotation −
In our example, node A has become unbalanced as a node is inserted in the right subtree of
A's right subtree. We perform the left rotation by making A the left-subtree of B.
Right Rotation
AVL tree may become unbalanced, if a node is inserted in the left subtree of the left subtree.
The tree then needs a right rotation.
As depicted, the unbalanced node becomes the right child of its left child by performing a
right rotation.
Left-Right Rotation
Double rotations are slightly complex version of already explained versions of rotations. To
understand them better, we should take note of each action performed while rotation. Let's
first check how to perform Left-Right rotation. A left-right rotation is a combination of left
rotation followed by right rotation.
State Action
A node has been inserted into the right subtree of the left subtree. This
makes C an unbalanced node. These scenarios cause AVL tree to
perform left-right rotation.
We first perform the left rotation on the left subtree of C. This makes
A, the left subtree of B.
We shall now right-rotate the tree, making B the new root node of this
subtree. C now becomes the right subtree of its own left subtree.
The tree is now balanced.
Right-Left Rotation
A node has been inserted into the left subtree of the right subtree. This
makes A, an unbalanced node with balance factor
2.
First, we perform the right rotation along C node, making C the right
subtree of its own left subtree B. Now, B becomes the right subtree of
A.
Linked List :
Node(1) -> Node(2) -> Node(3) -> Node(4) -> Node(5) -> Node(6) -> Node(7)
In linked list, the items are linked together through a single next pointer.
Next Node’s Key Can be larger or samller then previous Node.
Not Sorted
Time Complexity of Search :O(n) Insert : O(1) Delete : O(1) {if you have use optimized
method }
[4]
/ \
[2] [6]
/ \ / \
[1] [3] [5] [7]
Inorder traversal of expression tree produces infix version of given postfix expression (same with
preorder traversal it gives prefix expression)
Evaluating the expression represented by expression tree:
Let t be the expression tree
If t is not null then
If t.value is operand then
Return t.value
A = solve(t.left)
B = solve(t.right)
C++
#includestd>
using namespace std;
struct et
char value;
};
// is an operator
bool isOperator(char c)
if (c == '+' || c == '-' ||
c == '*' || c == '/' ||
c == '^')
return true;
return false;
if(t)
inorder(t->left);
inorder(t->right);
}
et* newNode(int v)
temp->value = v;
return temp;
};
// postfix expression
// input expression
if (!isOperator(postfix[i]))
t = newNode(postfix[i]);
st.push(t);
else // operator
t = newNode(postfix[i]);
t2 = st.top();
st.pop();
t->right = t1;
t->left = t2;
st.push(t);
// tree
t = st.top();
st.pop();
return t;
}
int main()
et* r = constructTree(postfix);
inorder(r);
return 0;
Tree Sort
Tree sort is a sorting algorithm that is based on Binary Search Tree data structure. It first creates a
binary search tree from the elements of the input list or array and then performs an in-order
traversal on the created binary search tree to get the elements in sorted order.
Algorithm:
Step 1: Take the elements input in an array.
Step 2: Create a Binary search tree by inserting data items from the array into the
binary search tree.
Step 3: Perform in-order traversal on the tree to get the elements in sorted order.
#includeiostream
struct Node
int key;
};
temp->key = item;
return temp;
// in arr[]
if (root != NULL)
arr[i++] = root->key;
return node;
// in arr[]
int i = 0;
int main()
int n = sizeof(arr)/sizeof(arr[0]);
treeSort(arr, n);
return 0;