Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 96

Unit 4

Tree
data structure

By: Dr. Saswati Debnath


TREE
• A tree usually represents the hierarchy of elements and depicts the relationships
between the elements. Trees are considered as one of the largely used facets of data
structures.
• Tree is a non-linear data structure. represents the hierarchical data.
• A tree is made up of nodes and edges.
Terminologies used in trees:

1. Root: The topmost node of a tree is called the root. There is no edge pointing to it, but one or
more than one edge originating from it. Here, A is the root node.
2. Parent: Any node which connects to the child. Node which has an edge pointing to some other
node. Here, C is the parent of H.
3. Child: Any node which is connected to a parent node. Node which has an edge pointing to it from
some other node. Here, H is the child of C.
4. Siblings: Nodes belonging to the same parent are called siblings of each other. Nodes B, C and D
are siblings of each other, since they have the same parent node A.
5. Ancestors: Nodes accessible by following up the edges from a child node upwards are called the
ancestors of that node. Ancestors are also the parents of the parents of …… that node. Here, nodes
A, C and H are the ancestors of node I.
6. Descendants: Nodes accessible by following up the edges from a parent node downwards are
called the descendants of that node. Descendants are also the child of the child of …… that node.
Here, nodes H and I are the descendants of node C.
• Leaf/ External Node: Nodes which have no edge originating from it, and have no
child attached to it. These nodes cannot be a parent. Here, nodes E, F, G and I are
leaf nodes.
• Internal node: Nodes with at least one child. Here, nodes B, D and C are internal
nodes.
• Depth: Depth of a node is the number of edges from root to that node. Here, the
depth of nodes A, C, H and I are 0, 1, 2 and 3 respectively.
• Height: Height of a node is the number of edges from that node to the deepest
leaf. Here, the height of node A is 3, since the deepest leaf from this node is node
I. And similarly, height of node C is 2.
1.A tree with n nodes has n-1 Why n-1?
• Because in a tree, there is one and only edge corresponding to all the nodes except
the root node. The root node has no parent, hence no edge pointing to it.
Therefore, a total of n-1 edges.
2.The degree of a node in a tree is the number of children of a node.
3.The degree of a tree is the highest degree of a node among all the nodes
present in the tree.
Binary Tree:
• Binary Tree: A binary tree is a special type of tree where each node has a degree
equal to or less than two which means each node should have at most two
children.

Example 1 has nodes A, B, C, D, E with degrees {2, 0, 1, 1, 0} respectively which satisfies the
conditions for a binary tree. Similarly, example 2 has nodes A and B, having degrees 1 each,
hence a binary tree.
What is a binary tree?
• Property 1: each node can have up to two
successor nodes.
What is a binary tree? (cont.)
• Property 2: a unique path exists from the root to
every other node

Not a valid binary tree!


Some terminology
• The successor nodes of a node are called its children
• The predecessor node of a node is called its parent
• The "beginning" node is called the root (has no parent)
• A node without children is called a leaf
Some terminology (cont’d)
• Nodes are organize in levels (indexed from 0).

• Level (or depth) of a node: number of edges in the path


from the root to that node.

• Height of a tree h: #levels = L not full!

(Warning: some books define h


as #levels-1).

• Full tree: every node has exactly


two children and all the
leaves are on the same level.
Types of Binary Trees

• Full or Strict Binary trees: Binary trees as we said earlier have a degree of 2 or
less than 2. But a strict binary tree is a binary tree having all of its nodes with a degree of
2 or 0. That is each of its nodes either have 2 children or is a leaf node.
this binary tree is not a strict or full binary tree because the colored node has a
degree of just 1.
• Perfect Binary Tree: A perfect binary tree has all its internal nodes with degree strictly
2 and has all its leaf nodes on the same level. A perfect binary tree as the name suggests
appears exactly perfect.
This binary tree is not a perfect binary tree because the colored node is
an internal node and has a degree of just 1, although each leaf node is on
the same level.
• Complete Binary Tree: A complete binary tree has all its levels completely filled
except possibly the last level. And if the last level is not completely filled then the last
level’s keys must be all left-aligned.
• Degenerate tree: The easiest of all, degenerate trees are binary trees where
every parent node has just one child and that can be either to its left or right.
• Skewed trees: Skewed trees are binary trees where every parent node has just a single
child and that child should be strict to the left or to the right for all the parents. Slewed
trees having all the child nodes aligned to the left are called left-skewed trees, and skewed
trees having all the child nodes aligned to the right are called right-skewed trees.
What is the max nodes
at some level l?

The max #nodes at level l is 2l where l=0,1,2, ...,L-1

 20
 21
 22
 23
What is the total nodes N
of a full tree with height h?

0 1 h 1 h
N  2  2  ...  2  2 1
l=0 l=1 l=h-1

using the geometric series:


n 1
0 1
x  x  ...  x n 1
 x  i x n 1
x 1
i 0
What is the height h
of a full tree with N nodes?

h
2 1  N
h
 2  N 1
 h  log( N  1)  O (log N )
Array representation of Binary trees: 
• Arrays are linear data structures and for arrays to function, their size must be specified
before elements are inserted into them. And this counts as the biggest demerit of
representing binary trees using arrays. Suppose you declare an array of size 100, and after
storing 100 nodes in it, you cannot even insert a single element further, regardless of all
the spaces left in the memory.  
Linked Representation of Binary Trees:
• This method of representing binary trees using linked nodes is considered the most
efficient method of representation. For this, we use doubly-linked lists.  Using links makes
the understanding of a binary tree very easy. It actually makes us visualize the tree even.
Suppose we have a binary tree of 3 levels.
a doubly linked list helped us traversing both to the left and the right. And using
that we would create a similar node here, pointing both to the left and the right
child node.
• this representation resembles a real tree node, unlike the array representation where all the nodes
succumbed to a 2D structure. And now we can very easily transform the whole tree into its linked
representation which is just how we imagined it would have looked in real life.
So, this was the representation of the binary tree we saw above using linked representation.
And what are these nodes? These are structures having three structure members, first a data
element to store the data of the node, and then two structure pointers to hold the address of
the child nodes, one for the left, and the other for the right.
How to create binary tree using C
• struct node{
• int data;
• struct node* left;
• struct node* right;
• };
• #include<stdio.h>
#include<malloc.h>

struct node{
int data;
struct node* left;
struct node* right;
};

struct node* createNode(int data)


•{
struct node *n; // creating a node pointer
n = (struct node *) malloc(sizeof(struct node)); // Allocating memory in the heap
n->data = data; // Setting the data
n->left = NULL; // Setting the left and right children to NULL
n->right = NULL; // Setting the left and right children to NULL
return n; // Finally returning the created node
}

int main(){
/*
// Constructing the root node
struct node *p;
p = (struct node *) malloc(sizeof(struct node));
p->data = 2;
p->left = NULL;
p->right = NULL;
• // Constructing the second node
struct node *p1;
p1 = (struct node *) malloc(sizeof(struct node));
p->data = 1;
p1->left = NULL;
p1->right = NULL;

// Constructing the third node


struct node *p2;
p2 = (struct node *) malloc(sizeof(struct node));
p->data = 4;
p2->left = NULL;
p2->right = NULL;
*/

// Constructing the root node - Using Function (Recommended)


struct node *p = createNode(2);
struct node *p1 = createNode(1);
struct node *p2 = createNode(4);

// Linking the root node with left and right children


p->left = p1;
p->right = p2;
return 0;
}
Traversal in Binary Tree (InOrder, PostOrder and PreOrder Traversals)
Pre Order Traversal in a Binary Tree:

• So in this traversal technique, the first node you start with is the root node. And thereafter you visit
the left subtree and then the right subtree. Taking the above example, I’ll mark your order of
traversal as below. You first visit section 1, then 2, and then 3
• Each time you get a tree, you first visit its root node, and then move to its left subtree, and then
to the right.
• So, here you first visit the root node element 7 and then move to the left subtree. The left subtree
in itself is a tree with root node 11. So, you visit that and move further to the left subtree of this
subtree. There you see a single element 7, you visit that, and then move to the right subtree
which is a NULL. So, you're finished with the left subtree of the main tree. Now, you move to the
right subtree which has element 2 as its node. And then a NULL to its left and element 9 to its
right.
• So basically, you recursively visit each subtree in the same order. And your final traversal order is:
• // Constructing the root node - Using Function (Recommended)
• struct node *p = createNode(4);
• struct node *p1 = createNode(1);
• struct node *p2 = createNode(6);
• struct node *p3 = createNode(5);
• struct node *p4 = createNode(2);
• // Finally The tree looks like this:
• // 4
• // / \
• // 1 6
• // / \
• // 5 2

• // Linking the root node with left and right children


• p->left = p1;
• p->right = p2;
• p1->left = p3;
• p1->right = p4;
• void preOrder(struct node* root){
• if(root!=NULL){
• printf("%d ", root->data);
• preOrder(root->left);
• preOrder(root->right);
• }
•}
• #include<stdio.h>
• #include<malloc.h>
struct node{
• int data;
• struct node* left;
• struct node* right;
• };
• struct node* createNode(int data){
• struct node *n; // creating a node pointer
• n = (struct node *) malloc(sizeof(struct node)); // Allocating memory in the heap
• n->data = data; // Setting the data
• n->left = NULL; // Setting the left and right children to NULL
• n->right = NULL; // Setting the left and right children to NULL
• return n; // Finally returning the created node
•}

• void preOrder(struct node* root){


• if(root!=NULL){
• printf("%d ", root->data);
• preOrder(root->left);
• preOrder(root->right);
• }
•}
• int main(){
• // Constructing the root node - Using Function (Recommended)
• struct node *p = createNode(4);
• struct node *p1 = createNode(1);
• struct node *p2 = createNode(6);
• struct node *p3 = createNode(5);
• struct node *p4 = createNode(2);

• // Linking the root node with left and right children


• p->left = p1;
• p->right = p2;
• p1->left = p3;
• p1->right = p4;

• preOrder(p);
• return 0;
• }
Post Order Traversal in a Binary Tree:
• In this traversal technique, things are quite opposite to the PreOrder traversal. Here, you first visit
the left subtree, and then the right subtree. So, the last node you’ll visit is the root node. first visit
section 1, then 2, and then 3.
• Each time you get a tree, you first visit its left subtree, and then its
right subtree, and then move to its root node.
InOrder Traversal in a Binary Tree:
• In this traversal technique, we simply start with the left subtree, that
is you first visit the left subtree, and then go to the root node and
then you’ll visit the right subtree.
• Each time you get a tree, you first visit its left subtree, and then its
root node, and then finally its right subtree.
How to search a binary tree?
(1) Start at the root
(2) Search the tree level
by level, until you find
the element you are
searching for or you reach
a leaf.

Is this better than searching a linked list?

No  O(N)
Binary Search Trees (BSTs)
• Binary Search Tree Property:
The value stored at
a node is greater than
the value stored at its
left child and less than
the value stored at its
right child
Binary Search Trees (BSTs)

In a BST, the value


stored at the root of
a subtree is greater
than any value in its
left subtree and less
than any value in its
right subtree!
Binary Search Trees (BSTs)

Where is the
smallest element?
Ans: leftmost element

Where is the largest


element?
Ans: rightmost element
How to search a binary search tree?
(1) Start at the root
(2) Compare the value of
the item you are
searching for with
the value stored at
the root
(3) If the values are
equal, then item
found; otherwise, if it
is a leaf node, then
not found
How to search a binary search tree?
(4) If it is less than the value
stored at the root, then
search the left subtree
(5) If it is greater than the
value stored at the root,
then search the right
subtree
(6) Repeat steps 2-6 for the
root of the subtree chosen
in the previous step 4 or 5
How to search a binary search tree?

Is this better than searching


a linked list?

Yes !! ---> O(logN)


Tree node structure

template<class ItemType>
struct TreeNode<ItemType> {
ItemType info;
TreeNode<ItemType>* left;
TreeNode<ItemType>* right;
};
Binary Search Tree Specification
#include <fstream.h>
 
struct TreeNode<ItemType>;
 
enum OrderType {PRE_ORDER, IN_ORDER, POST_ORDER};
 
template<class ItemType>
class TreeType {
public:
TreeType();
~TreeType();
TreeType(const TreeType<ItemType>&);
void operator=(const TreeType<ItemType>&);
void MakeEmpty();
bool IsEmpty() const;
bool IsFull() const;
int NumberOfNodes() const;
Binary Search Tree Specification
(cont.)
void RetrieveItem(ItemType&, bool& found);
void InsertItem(ItemType);
void DeleteItem(ItemType);
void ResetTree(OrderType);
void GetNextItem(ItemType&, OrderType, bool&);
void PrintTree(ofstream&) const;
private:
TreeNode<ItemType>* root;
};
 
Insertion in a Binary Search Tree
There are no duplicates in a binary search tree. 
Here is an example binary search tree, and the element
we want to insert is 9
• Now, you would simply start from the root node, and see if the element you want to insert is
greater than or less than. And since 9 is greater than 8, we move to the right of the root. And then
the root is the element 10, and since this time 9 is less than 10, we move to the left of it. And since
there are no elements to its left, we simply insert element 9 there.
Now, before you insert a node, the first thing you would do is to create that node and allocate memory to it in
heap using malloc. Then you would initialize the node with the data given, and both the right and the left
member of the node should be marked NULL.

And another important thing to see here is the pointer you would follow the correct position with. In the
above example, to be able to insert at that position, the pointer must be at node 10.
• void insert(struct node *root, int key){
• struct node *prev = NULL;
• while(root!=NULL){
• prev = root;
• if(key==root->data){
• printf("Cannot insert %d, already in BST", key);
• return;
• }
• else if(key<root->data){
• root = root->left;
• }
• else{
• root = root->right;
• }
• }
• struct node* new = createNode(key);
• if(key<prev->data){
• prev->left = new;
• }
• else{
• prev->right = new;
• }
•}
Deletion in a Binary Search Tree
whenever we talk about deleting a node from binary search tree, we have
the following three cases in mind:
• The node is a leaf node.
• The node is a non-leaf node.
• The node is the root node.
• Deleting a leaf node:
• Deleting a leaf node is the simplest case in deletion in binary search
trees where the only thing you have to do is to search the element in
the tree, and remove it from the tree, and make its parent node point
to NULL. To be more specific, follow the steps below to delete a leaf
node along with the illustrations of how we delete a leaf node in the
above tree:

• Search the node.


• Delete the node.
Deleting a non-leaf node:
Now suppose the node is not a leaf node, so you cannot just make its parent point to NULL,
and get away with it. You have to even deal with the children of this node.
1.So, the first thing you would do is to search element 6.
• Now the dilemma is, which node will replace the position of
node 6. Well, there is a simple answer to it. It says, when you
delete a node that is not a leaf node, you replace its position
with its InOrder predecessor or Inorder successor.
• So, if you write the InOrder traversal of the tree, you will get:
• 1→ 3→ 4 →6 →7→ 8→ 10→ 13→ 14
• So, the InOrder predecessor and the Inorder successor of node
6 are 4 and 7 respectively. Hence you can substitute node 6
with any of these nodes, and the tree will still be a valid binary
search tree.
So, both are still binary search trees. In the first case, we replaced node 6 with node 4. And the
right subtree of node 4 is 7, which is still bigger than it. And in the second case, we replaced
node 6 with node 7. And the left subtree of node 7 is 4, which is still smaller than the node.
Hence, a win-win for us.
Deleting the root node: 
2. the first thing you do is write the InOrder traversal of the whole tree. And then
replace the position of the root node with its InOrder predecessor or Inorder
successor. So, here the traversal order is,
• 1→ 3→ 4 →6 →7→ 8→ 10→ 13→ 14
• So, the InOrder predecessor and the Inorder successor of the root node 8 are
7 and 10 respectively. Hence you can substitute node 8 with any of these
nodes, but there is a catch here. So, if you substitute the root node here, with
its InOrder predecessor 7, the tree will still be a binary search tree, but when
you substitute the root node here, with its InOrder successor 10, there still
becomes an empty position where node 10 used to be. So, we still placed the
InOrder successor of 10, which was 13 on the position where 10 used to be.
And then there are no empty nodes in between. This finalizes our deletion.
So, there are a few steps:
1.First, search for the node to be deleted.
2.Search for the InOrder Predecessor and Successor of the node.
3.Keep doing that until the tree has no empty nodes.
Insertion of a key
Insertion of a key
• 1. Start from the root.
• 2. Compare the inserting element with root, if less than root, then
recurse for left, else recurse for right.
• 3. After reaching the end, just insert that node at left(if less than
current) else right.
import java.util.*;
import java.io.*;
 
class GFG {
    public static void main (String[] args) {
         BST tree=new BST();
        tree.insert(30);
        tree.insert(50);
        tree.insert(15);
        tree.insert(20);
        tree.insert(10);
        tree.insert(40);
        tree.insert(60);
        tree.inorder();
    }
}
Tree Traversals (Inorder, Preorder and
Postorder)
• Depth First Traversals:
• (a) Inorder (Left, Root, Right) : 4 2 5 1 3
• (b) Preorder (Root, Left, Right) : 1 2 4 5 3
• (c) Postorder (Left, Right, Root) : 4 5 2 3 1
• Breadth First or Level Order Traversal : 1 2 3 4 5
In-order traversal
• 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)
Pre-order Traversal:
• Algorithm Pre-order(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 Pos-torder (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.
Creating Binary Tree

• // Constructing the root node - Using Function (Recommended)


• struct node *p = createNode(4);
• struct node *p1 = createNode(1);
• struct node *p2 = createNode(6);
• struct node *p3 = createNode(5);
• struct node *p4 = createNode(2);
• // Finally The tree looks like this:
• // 4
• // / \
• // 1 6
• // / \
• // 5 2

• // Linking the root node with left and right children


• p->left = p1;
• p->right = p2;
• p1->left = p3;
• p1->right = p4;
Creating the preOrder function

• void preOrder(struct node* root){


• if(root!=NULL){
• printf("%d ", root->data);
• preOrder(root->left);
• preOrder(root->right);
• }
•}
• #include<stdio.h>
• #include<malloc.h>

• struct node{
• int data;
• struct node* left;
• struct node* right;
• };
• struct node* createNode(int data){
• struct node *n; // creating a node pointer
• n = (struct node *) malloc(sizeof(struct node)); // Allocating memory in the heap
• n->data = data; // Setting the data
• n->left = NULL; // Setting the left and right children to NULL
• n->right = NULL; // Setting the left and right children to NULL
• return n; // Finally returning the created node
•}
• void preOrder(struct node* root){
• if(root!=NULL){
• printf("%d ", root->data);
• preOrder(root->left);
• preOrder(root->right);
• }
•}
•int main(){
• // Constructing the root node - Using Function (Recommended)
• struct node *p = createNode(4);
• struct node *p1 = createNode(1);
• struct node *p2 = createNode(6);
• struct node *p3 = createNode(5);
• struct node *p4 = createNode(2);
• // Finally The tree looks like this:
• // 4
• // / \
• // 1 6
• // / \
• // 5 2
• // Linking the root node with left and right children
• p->left = p1;
• p->right = p2;
• p1->left = p3;
• p1->right = p4;
preOrder(p);
• return 0;
•}
Creating the postOrder function

• void postOrder(struct node* root){


• if(root!=NULL){
• postOrder(root->left);
• postOrder(root->right);
• printf("%d ", root->data);
• }
•}
Creating the InOrder function

• void inOrder(struct node* root){


• if(root!=NULL){
• inOrder(root->left);
• printf("%d ", root->data);
• inOrder(root->right);
• }
•}
Binary Search Trees
• It is a type of binary tree.
• All nodes of the left subtree are lesser than the node itself.
• All nodes of the right subtree are greater than the node itself.
• Left and Right subtrees are also binary trees.
• There are no duplicate nodes.
is this a binary search tree ?
• Answer is no. Because the left subtree is good but the right subtree of
the root node is lesser than the root node itself violating the 3rd
property.
Is this a binary search tree? Why?
• Why?
• The very first thing to observe here is the properties of a Binary Search Tree. You
would check if all the properties are satisfied for each of the nodes of the tree. So,
you first start with the root node which is element 9 and see if all the nodes on the
left subtree {4, 2, 5, 7} are smaller than 9 and all the nodes of the right subtree
{11, 15, 14} are greater than 9. And since they are, we’ll proceed with the next
node. Doing this for all the nodes, we’ll conclude that this is a Binary Search Tree.
Had there been even one violation for any of the nodes, we would have said, no.
the InOrder traversal of a binary search tree gives an ascending sorted array. So, this is one of the easiest ways to check if a tree is a binary search tree.

So, the final InOrder traversal order of the above tree is


2 → 4 → 5 → 6 → 7 → 8 → 9 → 11 → 14 → 15,
which is obviously in increasingly sorted order. Hence, it is a binary search tree. And this is how easy we
have made checking if a tree is a binary search tree or not.
How to search a binary search tree?
(1) Start at the root
(2) Compare the value of
the item you are
searching for with
the value stored at
the root
(3) If the values are
equal, then item
found; otherwise, if it
is a leaf node, then
not found
How to search a binary search tree?
(4) If it is less than the value
stored at the root, then
search the left subtree
(5) If it is greater than the
value stored at the root,
then search the right
subtree
(6) Repeat steps 2-6 for the
root of the subtree chosen
in the previous step 4 or 5
How to search a binary search tree?

Is this better than searching


a linked list?

Yes !! ---> O(logN)


Searching in a Binary Search Trees
• The key we want to search in this binary search tree is 55.  Let’s start our search. So, we’ll first
compare our key with the root node itself, which is 50.
• But since 50 is less than 55, which side should we proceed with? Left, or Right? Of
course, right. Since all the elements in the right subtree of a node are greater than that
node, we’ll move to the right. The first element we check our key with is 60.
• Now, since our key is smaller than 60, we’ll move to the left of the current node. The Left
subtree of 60 contains only one element and since that is equal to our key, we revert the
positive result that yes, the key was found. Had this leaf node been not equal to the key,
and since there are no subtrees further, we would have stopped here itself with negative
results, saying the key was not found.
• one more key which is 45. Now, I’ll directly illustrate the path we
followed and whether it was found or not.

We went to 50 and found it smaller so moved to its left.


Then we found 40, and since our key was greater than
that, we moved to its right, and the leaf node we found
was equal to 45 only.
• Time Complexity of the Search Operation in a Binary Search
Tree:
• we had studied an algorithm called the Binary Search. We could
use that algorithm only if the array we were searching for some
key in was sorted. And that algorithm had the time complexity
of O(logn) where n was the length of the array. Because we
were always dividing our search space into half on the basis of
whether our key was smaller or greater than the mid.  And as
you might have guessed, searching in a binary search tree is
very much similar to that.
• Searching in a binary search tree holds O(logn) time complexity
in the best case where n is the number of nodes making it
incredibly easier to search an element in a binary search tree,
and even operations like inserting get relatively easier.
• Let’s calculate exactly what happens. If you could see the above examples, the
algorithm took the number of comparisons equal to the height of the binary search
tree, because at each comparison we stepped down the depth by 1. So, the time
complexity T ∝ h, that is, our time complexity is proportional to the height of the tree.
Therefore, the time complexity becomes O(h).

• Now, if you remember, the height of a tree ranges from logn to n, that is

• (logn) ≤ h ≤ n

• So, the best-case time complexity is O(logn) and the worst-case time complexity is O(n).
Pseudocode for searching in a Binary Search Tree:

You might also like