Binary Tree:: First Simple Tree in C
Binary Tree:: First Simple Tree in C
Binary Tree:: First Simple Tree in C
Since each element in a binary tree can have only 2 children, we typically name
them the left and right child.
int data;
----
1 <-- root
/ \
2 3
C Implementation of tree
struct node
{
int data;
struct node *left;
struct node *right;
};
/* newNode() allocates a new node with the given data and NULL left and
right pointers. */
struct node* newNode(int data)
{
// Allocate memory for new node
struct node* node = (struct node*)malloc(sizeof(struct node));
int main()
{
/*create root*/
struct node *root = newNode(1);
/* following is the tree after above statement
1
/ \
NULL NULL
*/
root->left = newNode(2);
root->right = newNode(3);
/* 2 and 3 become left and right children of 1
1
/ \
2 3
/ \ / \
NULL NULL NULL NULL
*/
root->left->left = newNode(4);
/* 4 becomes left child of 2
1
/ \
2 3
/ \ / \
4 NULL NULL NULL
/ \
NULL NULL
*/
getchar();
return 0;
}
Properties of Binary Tree
1) The maximum number of nodes at level ‘l’ of a binary tree is 2l-1.
Here level is number of nodes on path from root to the node (including root and node). Level of root is
1.
This can be proved by induction.
For root, l = 1, number of nodes = 21-1 = 1
Assume that maximum number of nodes on level l is 2l-1
l-1
Since in Binary tree every node has at most 2 children, next level would have twice nodes, i.e. 2*2
5) In Binary tree, number of leaf nodes is always one more than nodes with two
children.
L = T + 1
/ \
15 30
/ \ / \
40 50 100 40
b) 18
/ \
15 20
/ \
40 50
/ \
30 50
c) 18
/ \
40 30
/ \
100 40
2-Complete Binary Tree or Perfect Binary Tree -- A Binary tree is Perfect Binary Tree in
which all internal nodes have two children and all leaves are at same level.
Practical example of Complete Binary Tree is Binary Heap
Following are examples of Perfect Binaryr Trees.
a) 18
/ \
15 30
/ \ / \
40 50 100 40
b) 18
/ \
15 30
A Perfect Binary Tree of height h (where height is number of nodes on path from root to leaf)
has 2h– 1 node.
at every level of complete binary tree there must be 2level number of nodes
Example of Perfect binary tree is ancestors in family. Keep a person at root, parents as children,
parents of parents as their children.
3. Extended Binary Tree
A binary tree can be converted into Full Binary tree by adding dummy nodes to
existing nodes wherever required.
The full binary tree obtained by adding dummy nodes to a binary tree is called as
Extended Binary Tree.
In above figure, a normal binary tree is converted into full binary tree by adding
dummy nodes (In pink colour).
5-A degenerate (or pathological) tree -- A Tree where every internal node has one child.
Such trees are performance-wise same as linked list.
10
20
30
40
Binary Tree (Threaded And Unthreaded ) as Data Structure
1- Threaded Binary Tree-
a) Inorder traversal of a Binary tree is either be done using recursion or with the use of a
auxiliary stack.
b) The idea of threaded binary trees is to make inorder traversal faster and do it without
stack and without recursion.
c) A binary tree is made threaded by making all right child pointers that would normally
be NULL point to the inorder successor of the node (if it exists).
d) There are two types of threaded binary trees-
1) Single Threaded: Where a NULL right pointers is made to point to the inorder
successor (if successor exists)
2) Double Threaded: Where both left and right NULL pointers are made to point to
inorder predecessor and inorder successor respectively.
The predecessor threads are useful for reverse inorder traversal and postorder traversal.
The threads are also useful for fast accessing ancestors of a node.
Following diagram shows an example Single Threaded And Double Threaded Binary Tree.
The dotted lines represent threads.
return n;
}
// C code to do inorder traversal in a threaded binary tree
void inOrder(struct Node *root)
{
struct Node *cur = leftmost(root);
while (cur != NULL)
{
printf("%d ", cur->data);
/ \
5 100
/ \
80 120
and stack)
In inorder traversal, we follow “left root right”. We can move to children using left and right
pointers. Once a node is visited, we need to move to parent also. For example, in the above
tree, we need to move to 10 after printing 5. For this purpose, we use parent pointer.
Below is algorithm.
Illustration:
Let us consider below tree for illustration.
10
/ \
5 100
/ \
80 120
Description of Illustration
to 80 (3.a) and set leftdone as true. We print current node 80 and move back to parent 100
(3.d). Since leftdone
#include <queue>
struct Node {
int key;
};
eturns pointer */
temp->key = key;
};
if (!temp)
return;
inorder(temp->left);
inorder(temp->right);
queue<struct Node*> q;
q.push(temp);
// an empty place.
while (!q.empty()) {
q.pop();
if (!temp->left) {
temp->left = newNode(key);
break;
else q.push(temp->left);
if (!temp->right) {
temp->right = newNode(key);
break;
} else q.push(temp->right);
}
// Driver code
int main()
root->left = newNode(11);
root->left->left = newNode(7);
root->right = newNode(9);
root->right->left = newNode(15);
root->right->right = newNode(8);
inorder(root);
insert(root, key);
inorder(root);
return 0;
Output:
struct Node
int key;
};
return pointer */
temp->key = key;
return temp;
};
if (!temp)
return;
inorder(temp->left);
cout << temp->key << " ";
inorder(temp->right);
queue<struct Node*> q;
q.push(root);
while(!q.empty())
temp = q.front();
q.pop();
if (temp->right)
if (temp->right == d_node)
temp->right = NULL;
delete(d_node);
return;
else q.push(temp->right);
if (temp->left)
if (temp->left == d_node)
temp->left=NULL;
delete(d_node);
return;
}
else q.push(temp->left);
queue<struct Node*> q;
q.push(root);
while (!q.empty())
temp = q.front();
q.pop();
if (temp->key == key)
key_node = temp;
if (temp->left)
q.push(temp->left);
if (temp->right)
q.push(temp->right);
int x = temp->key;
deletDeepest(root, temp);
key_node->key = x;
// Driver code
int main()
root->left->left = newNode(7);
root->left->right = newNode(12);
root->right = newNode(9);
root->right->left = newNode(15);
root->right->right = newNode(8);
inorder(root);
deletion(root, key);
inorder(root);
return 0;
Output:
Example Tree
Uses of Inorder
In case of binary search trees (BST), Inorder traversal gives nodes in non-decreasing order.
To get nodes of BST in non-increasing order, a variation of Inorder traversal where Inorder
traversal is reversed, can be used.
Example: Inorder traversal for the above given figure is 4 2 5 1 3.
(ii)Preorder Traversal:
Algorithm Preorder(tree)
Uses of Preorder
Preorder traversal is used to create a copy of the tree.
Preorder traversal is also used to get prefix expression on of an expression tree.
Example: Preorder traversal for the above given figure is 1 2 4 5 3.
(iii)Postorder Traversal:
Algorithm Postorder(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.
Example: Postorder traversal for the above given figure is 4 5 2 3 1.
Output:
1 2 4 5 3
4 2 5 1 3
4 5 2 3 1
Constructing Binary Tree From Traversal Result-
*We can construct a binary tree if we are given at least two traversal results. The first
traversal must be the in-order traversal and the second can be either pre-order or post-
order traversal.
* The in-order traversal result will be used to determine the left and the right child nodes, and
the
* pre-order/post-order can be used to determine the root node. For example, consider the
traversal results given below:
Inorder-INFORMATION
Postorder-INOFMAINOTR
Pre–order Traversal: A B D E C F G
Here, we have the in-order traversal sequence and pre-order traversal sequence. Follow the
steps given below to construct the tree:
In Pre-Order the first element, in Post-Order the last element is the root node.
PreOrder-Root node ←A B D E C F G
Elements on the left side of the root node in the in-order traversal form the left sub-tree of the
root node. Similarly, elements on the right side of the root node in the in-order traversal
sequence form the right sub-tree of the root node.
Step 3
Preorder- Recursively select each element from pre-order traversal(make its parent node) ,
from left to right and create its left and right sub-trees from the in-order traversal.
Postorder- Recursively select each element from post-order traversal, from right to left and
create its left and right sub-trees from in-order traversal.
Example 1- of In-order and Post-Order Traversal
In–order Traversal: D B H E I A F J C G
Post-order Traversal: D H E B I J F G C
AVL Tree
1-The AVL tree was introduced in the year of 1962 by G.M. Adelson-Velsky and
E.M. Landis.
2-AVL tree is a self balanced binary search tree. That means, an AVL tree is also a
binary search tree but it is a balanced tree.
3-A binary tree is said to be balanced, if the difference between the hieghts of
left and right subtrees of every node in the tree is either -1, 0 or +1.
In other words, a binary tree is said to be balanced if for every node, height of its
children differ by at most one.
4-In an AVL tree, every node maintains a extra information known as balance
factor.
An AVL tree is a balanced binary search tree. In an AVL tree, balance factor of
every node is either -1, 0 or +1.
6-Balance Factor- Balance factor of a node is the difference between the heights
of left and right subtrees of that node. The balance factor of a node is calculated
either height of left subtree - height of right subtree (OR) height of right subtree -
height of left subtree.
In the following explanation, we are calculating as follows...
The above tree is a binary search tree and every node is satisfying balance
factor condition. So this tree is said to be an AVL tree.
Every AVL Tree is a binary search tree but all the Binary Search Trees need
not to be AVL trees.
Step 1: Insert the new element into the tree using Binary Search Tree
insertion logic.
Step 2: After insertion, check the Balance Factor of every node.
Step 3: If the Balance Factor of every node is 0 or 1 or -1 then go for next
operation.
Step 4: If the Balance Factor of any node is other than 0 or 1 or -1 then
tree is said to be imbalanced. Then perform the suitable Rotation to make
it balanced. And go for next operation.
(a)Note that the last (deepest) node in a tree deleted is a leaf or a node with one
child
* Then trace the path from the new leaf towards the root
* For each node x encountered, check if heights of left(x) and right(x) differ by at most
1.
We speak of the tree as being composed of nodes, each of which contains an entry which is a
member of the set being represented, a left branch and a right branch. A tree is a binary
search tree with respect to a given total ordering relation if the entry at any node is greater
than all entries occurring in the left branch, and less than all entries occurring in the right
branch. This is the first data invariant for our representation of sets as trees.
A tree can also be an empty-tree, which has no entry, left branch or right branch.
The height of a tree is defined as being 0 for the empty tree, and one more than the maximum
of the height of the left branch and the height of right branch for a non-empty tree.
The great advantage of a binary search tree is that, if we are looking for a given entry, x in a
tree, we can compare x with the entry, y say, to find which sub-tree x must lie in.
If x=y then x is in the tree, and we have found it. If x<y then we know that it must lie in the
left branch of the tree if it is in the tree at all, and conversely, if x>y then it must lie in the
right branch. If the tree is adequately balanced, then at each stage we are dividing the size of
the set of values in which we are searching by 2, which means that our search for x will
terminate in logarithmic time.
However we may not have a set of size exactly 2h+1 - 1 elements. Such a set cannot be
represented by a perfectly balanced tree, but we can limit the amount of unbalance to
maintain logarithmic time access.
Any set can be represented by a tree in which the disparity between the number of entries in
the left branch and the number in the right branch is never more than 1. The algorithm to do
this is obvious enough:
If the set has an odd number of elements take the median element as the entry of
the tree, and recursively make a left sub-tree of all elements less than the median,
and a right sub-tree of all elements greater than the median.
If the set has an even number of elements, take one of the 2 elements nearest the
median, and recursively make left and right sub-trees less than and greater than this
element.
Let us call such a tree "well-balanced". If we kept our trees well-balanced, then this would
give us the shortest worst-case time to find a given element in a member_set? operation.