You are on page 1of 35

Data Structures and Algorithms

Binary Tree
Binary Tree Data Structure

What is Binary Tree Data Structure?


A binary tree is a data structure composed of nodes, each of which has at most two
children, the left child and the correct child. A binary tree's top node is referred to as
the root, and any following nodes are referred to as branches or sub-trees. Leaf
nodes are nodes that don't have any children. A binary tree is typically used to sort
and find data efficiently.

A binary tree organizes data in a hierarchical structure, similar to how you might
organize files in a file folder. The top node in a binary tree is called the root, and it
can have two child nodes (left and right). Each of those child nodes can also have its
child nodes, and so on.

For example, imagine you are creating a family tree. The root node would represent
the oldest generation (e.g., great-grandparents). The left child node would represent
the father, and the right child node would represent the mother. Each child node can
have its child nodes, representing the next generation (e.g., grandparents). And so
on, until you reach the leaf nodes, representing the youngest generation (e.g., you).
Why use Tree Data Structure?
Trees are a functional data structure because they allow for efficient organization
and manipulation of data. In addition, trees have a hierarchical structure, which
means that data can be organized in a way that makes it easy to find, sort, and
search for specific information.

For example, a binary search tree can be used to quickly find a specific value
because it's organized so that each node's value is higher than the values of the
others. Of all the nodes in its left subtree and less than the values of all the nodes in
its right subtree. This means you can eliminate half of the tree from your search with
each comparison you make.

In addition, trees can represent data in a way that mirrors real-world relationships
and hierarchies. For example, a family tree can represent the relationships between
family members, or an organizational chart can represent the hierarchy within a
company.

Binary Tree Representation

A pointer typically represents a binary tree's topmost node, also known as the tree's
root. The root node contains a value and pointers to its left and right child nodes.
Each child node can also have its child nodes, creating a hierarchical structure that
forms the binary tree. This pointer-based representation allows for efficient tree
traversals, insertions, and deletions.
Basic Operation On Binary Tree:

1. Traversal: visiting each node in the tree in a specific order, such as in-order,
pre-order, or post-order.
2. Insertion: adding a new node to the tree.
3. Deletion: removing a node from the tree.
4. Searching: finding a specific node in the tree based on its value.
5. Finding the Minimum and Maximum value: finding the node with the smallest
or largest value in the tree.
6. Finding the Height: Calculate a number of edges present between the root
and the farthest leaf node.
7. Finding the Depth: calculating the number of edges from the root to a specific
node.

Auxiliary Operations On Binary Tree:

1. Balancing: ensuring that the tree is balanced can improve the performance of
some operations.
2. Copy: creating a new tree that is identical to an existing tree
3. Compare: comparing two trees to check if they are identical
4. Inversion: creating a new tree with the left and right subtrees of the original
tree are swapped
5. Serialization and Deserialization: converting a tree to a string or a byte stream
representation for storage or transmission and vice-versa

Applications of Binary Tree:

1. File systems for organizing and managing files on a computer's hard drive
2. Database indexing for faster searching and sorting
3. Network routing to find the best path between two nodes
4. Game development for determining visibility, collision, and lighting
5. Data compression using Huffman coding
6. Artificial Intelligence using decision trees and random forests
7. Natural Language Processing using decision trees for parsing, semantic
analysis, and machine translation.

Binary Tree Traversals:

Tree traversal algorithms can be broadly classified into two categories:


1. Depth-first traversals: These traversals visit the tree's nodes by traversing
down each branch as far as possible before backtracking. The depth-first
traversals are in-order, pre-order, and post-order traversals.
2. Breadth-first traversals: These traversals visit the tree's nodes by traversing
the tree level by level. A typical example of breadth-first traversal is
level-order traversal, also known as a breadth-first search (BFS).

Depth-First Search (DFS) algorithm can be divided into three categories:

1. In-order traversal: visits the left subtree, the root node, and the right subtree.
This results in visiting the nodes in ascending order for a binary search tree.
2. Pre-order traversal: visits the root node, left, and right subtree. This is often
used to create a copy of the tree or to get a prefix expression of an expression
tree.
3. Post-order traversal: visits the left subtree, then the right subtree, and finally,
the root node. This is often used to delete the tree or to get a postfix
expression of an expression tree.

These three traversals are called DFS traversals, using the DFS algorithm to
traverse the tree. The DFS algorithm goes deep into the tree as much as possible
before backtracking.

Breadth-First Search (BFS) algorithm can be further classified into one


category:

1. Level-order traversal: This algorithm visits all the nodes of the tree level by
level, from left to right. It uses a queue data structure to keep track of the
following nodes to visit. It is also known as Breadth-First Search (BFS)

The BFS algorithm is mainly used for tasks such as finding the shortest path
between two nodes or searching for a specific node in the tree. Because it visits all
the nodes at each level before moving on to the next level, it is guaranteed to find
the shortest path if one exists.

Implementation of Binary Tree:

A binary tree can be implemented in Python using classes and objects.

Here is an example of how a binary tree can be implemented in Python:


This class defines a node in the binary tree with a value and two pointers to its left
and right child nodes. The __init__ method initializes the node with a value and sets
the left and right pointers to None.

In this example, a root node is created with a value of 10, and left and right child
nodes are added with values of 5 and 15, respectively. Then the left child of a left
child is added with value 2, and the left child of the right child is added with value 7.
Properties of Binary Tree

Properties of binary trees include:


1. Each node in the tree has at most two child nodes, which are referred to as
the left and right child nodes.
2. The left child node has a value less than or equal to its parent node, and the
right child node has a value greater than or equal to its parent node.
3. Each node in the tree can have zero or one parent node, except the root
node, which has no parent node.
4. The height of a binary tree is the number of edges from the root node to the
farthest leaf node.
5. A binary tree is considered balanced if the height of each node's left and right
subtrees differ by no more than one.
6. A binary tree is considered a complete binary tree if all levels of the tree are
filled, except possibly for the last level, and all nodes are left justified in that
level.
7. A binary tree is considered a full tree if every node has either 0 or 2 children.
8. A binary tree is considered perfect if it is both complete.
9. The number of leaf nodes in a binary tree is one less than the number of
nodes with two children (also known as internal nodes)

Types of Binary Tree

Types of Binary Trees based on the number of children:


Following are the types of Binary Tree based on the number of children:
1. Full Binary Tree: A binary tree in which every node has either 0 or 2 children.

2. Degenerate Binary Tree: A binary tree in which every internal node has one
child.

3. Skewed Binary Trees: A binary tree in which all the nodes are either
left-skewed or right-skewed, meaning that the tree is either entirely
left-leaning or entirely right-leaning.
These are the most common binary trees based on the number of children. Each of
these types has its unique properties and use cases and is used in different
situations based on the problem.

Types of Binary Tree based on the completion of levels:

Types of binary Tree based on the completion of levels:


1. Complete Binary Tree: A tree in which all levels are filled, except possibly for
the last level, and all nodes are left-justified in that level.

2. Perfect Binary Tree: A binary tree that is both complete.


3. Balanced Binary Tree: A binary tree in which the height of each node's left
and right subtrees differ by no more than one.

Some Special Types of Trees:

Some particular types of trees based on node values include:

1. Binary Search Tree (BST): A binary tree in which the value of each node is
greater than or equal to its left child and less than or equal to its right child.
This type of tree is often used for searching, sorting, and other operations that
require quick access to elements based on their values.

2. AVL Tree: A balanced binary search tree that maintains balance by rotating
nodes.
3. Red-Black Tree: A balanced binary search tree that maintains balance by
coloring nodes.

4. B-Tree: A self-balancing tree that allows for efficient insertion, deletion, and
searches operations on large data sets.
5. B+ Tree: A variation of the B-Tree optimized for operations such as insertion,
deletion, and search on large data sets and is commonly used in databases
and file systems.

6. Segment Tree: A specialised tree data structure used to query and update
ranges of values efficiently. It is often used for operations such as range sum,
range min/max, and range count in dynamic arrays.
Advantages of Binary Tree:

Binary trees have several advantages, including:

1. Efficiency: Binary trees can quickly search, insert, and delete elements,
making them a popular choice for data structures such as search trees and
priority queues.
2. Flexibility: Binary trees can represent various data structures, such as lists,
stacks, queues, and more.
3. Space efficiency: Binary trees can represent large data sets using a smaller
amount of memory than other data structures.
4. Easy to understand: The structure of a binary tree is relatively simple, making
it easy to understand and implement.
5. Easy to Traverse: Binary trees are easy to traverse, and the traversal can be
done in various ways, like in-order, pre-order, post-order, and level-order
traversals.
6. Can be balanced: Binary trees can be balanced, which can help keep the
tree's height as small as possible and improve the efficiency of operations.
7. Applications: Binary trees have a lot of applications in computer science. They
can be used in algorithms such as Huffman coding, Trie, and many more.
Disadvantages of Binary Tree:

Binary trees have several disadvantages, including:

1. Unbalanced trees can be inefficient: If a binary tree becomes unbalanced, it


can lead to poor performance for operations such as searching and inserting.
2. Space requirements: Though binary trees are space-efficient, they still require
more space than other data structures, such as arrays and linked lists for
small data sets.
3. Complexity: Implementing some operations on binary trees can be complex,
such as insertion and deletion in a balanced binary tree.
4. Limited to binary data: Binary trees can only have two children per node,
which can be limiting for certain types of data.
5. Traversal of extensive data can be a problem: Traversing a sizeable binary
tree can be problematic if the tree is not balanced and can lead to poor
performance.
6. Limited to specific use cases: Binary trees are not suitable for all data or use
cases and may be less efficient than other data structures for particular tasks.

Binary Tree (Array implementation)

Trees can be represented in two ways:

1. Dynamic Node Representation (Linked Representation): This Representation


uses a separate object for each node in the tree. Each node object contains a
value, a pointer to its left child, and a pointer to its right child. This
Representation allows for easy insertion and deletion of nodes and dynamic
resizing of the tree.

2. Array Representation (Sequential Representation): This Representation uses


an array to store the tree's nodes sequentially. The indices of the nodes in the
array define the parent-child relationship. The left child of a node is stored at
the index of 2i+1, and the right child of a node is stored at the index of 2i+2,
where i is the index of the parent node. This Representation is space efficient
and can be used for complete binary trees. But it's not as flexible as linked
Representation.
A binary tree can be implemented in Python using an array, which stores the tree
nodes
sequentially.

In this example, a BinaryTree class is defined, which has three data members, left
and right. Then, in the binary_tree_using_array function, the array is passed as an
argument, and each node's left and right children are assigned by recursively calling
the function with the appropriate array indices.

The function starts from the root, which is the first element of the array. For each
element in the array, it creates a new BinaryTree object, assigns the value of the
current element to it, and recursively calls the function for the left and right children
using the appropriate array indices.

What is a Binary Search Tree?

A Binary Search Tree (BST) is a binary tree in which the value of each node is
greater than or equal to its left child and less than or equal to its right child. This
property makes it easy to find specific elements in the tree quickly by comparing the
value of each node.
Imagine a tree where each node represents a number, and each branch represents
a comparison. To find a specific number, you start at the tree's root and compare the
number to the value of the root. If the number is smaller, you go to the left branch; if
it is more extensive, you go to the right branch. Repeat this process until you find the
number or reach a leaf node.

This type of tree is often used for searching, sorting, and other operations that
require quick access to elements based on their values. It is called a "search tree"
because it allows for efficient searching of elements in the tree.
It is important to note that this is a balanced tree, and the tree may not remain
balanced after insertions and deletions; we have different types of balanced binary
search trees, such as AVL, Red Black Tree, and others.

Properties of Binary Search Tree:

Here are some properties of a Binary Search Tree in simple terms:


1. Value of nodes: Each node in a Binary Search Tree is greater than or equal to
its left child and less than or equal to its right child.
2. Searching: BST is efficient for searching elements, as we can quickly find an
element by comparing its value to the value of each node.
3. Sorting: BST can be used for sorting elements, as elements can be easily
accessed in sorted order by traversing the tree in order.
4. Insertion and deletion: Elements can be easily inserted and deleted in a BST,
but it may cause the tree to become unbalanced.
5. Balanced Tree: A balanced tree is essential to ensure the efficiency of the
search operation, and we have different types of balanced binary search
trees, such as AVL, Red Black Tree, and others.
6. Height: The height of a well-balanced BST is log(n), where n is the number of
nodes in the tree.
7. The in-order traversal of a BST will give the elements in sorted order.
8. Unique elements: Each node of the BST should have a unique element.

These properties make the BST a functional data structure for operations that
require quick access to elements based on their values, such as searching, sorting,
and other functions.
Handling approach for Duplicate values in the Binary Search tree:

There are different approaches to handling duplicate values in a Binary Search Tree
(BST) depending on the specific requirements of the application
1. Ignore duplicate values
2. Store duplicate values in the same node
3. Store duplicate values in a separate node
4. Store duplicate values in the node's left or right child with the original value.

Below are the various operations that can be performed on a BST:

Several operations can be performed on a Binary Search Tree (BST). Here are a few
common ones:

Insert a node into a BST:

Inserting a new node into a Binary Search Tree (BST) involves finding the
appropriate position for the new node based on its value and then creating and
linking the new node to the existing tree.
In this example, a Node class is defined, which has three data members, data left
and right. The insert function takes the root of the BST and the value to be inserted
as parameters.

The function starts by checking if the root is None, in which case it creates a new
node with the given value and returns it. Otherwise, it compares the value of the new
node to the value of the current node. If the new value is less than the current value,
it recursively calls the insert function on the left child. If the new value is greater than
the current value, it recursively calls the insert function on the right child. This
process continues until it reaches a leaf node, where it creates a new node and
attaches it to the appropriate child of the leaf node.

Inorder traversal:
An in-order traversal of a Binary Search Tree (BST) visits the tree nodes in the order
of the left, root, and right child.

This example defines a node class with three data members, left and right. The in
order function takes the root of the BST as a parameter and recursively visits the left
subtree, the root, and the right subtree.
The function starts by checking if the root is None, in which case it returns.
Otherwise, it recursively calls the inorder function on the left child. Then it prints the
current node's data and recursively calls the inorder function on the right child.

This process continues until all the tree nodes are visited, and the data of each node
is printed in the order of the left child, root, and right child.

Preorder traversal:
Preorder traversal of a Binary Search Tree (BST) visits the tree's root first, then the
left, and finally the right.

In this example, a Node class is defined, which has three data members, data left
and right. The preorder function takes the root of the BST as a parameter and
recursively visits the root, left subtree, and right subtree.

The function starts by checking if the root is None, in which case it returns.
Otherwise, it prints the current node's data, then recursively calls the preorder
function on the left child, and finally, it recursively calls the preorder function on the
right child.

This process continues until all the tree nodes are visited, and the data of each node
is printed in the order of root, left child, and right child.

Postorder traversal:

Postorder traversal of a Binary Search Tree (BST) visits the left subtree first, then the
right subtree, and finally, the tree's root.

In this example, a Node class is defined, which has three data members, data left
and right. The postorder function takes the root of the BST as a parameter and
recursively visits the left subtree, then the right subtree, and finally, the tree's root.
The function starts by checking if the root is None, in which case it returns.
Otherwise, it recursively calls the postorder function on the left child, then on the
right child, and finally, it prints the current node's data.

This process continues until all the tree nodes are visited, and the data of each node
is printed in the order of the left child, right child, and root.
Level order traversal:
Level-order traversal of a Binary Search Tree (BST) visits the nodes of the tree level
by level, from top to bottom.

In this example, a Node class is defined, which has three data members, data left
and right. The level order function takes the root of the BST as a parameter and
visits the nodes of the tree level by level.

The function uses a queue to keep track of the nodes to be visited at each level. It
starts by adding the root to the queue and then enters a while loop. In each iteration,
it prints the node's data at the front of the queue and then removes it from the queue.
It then adds the left and right children of the node to the queue if they exist.

This process continues until all the tree nodes are visited in level order.
Print nodes at given Level :
You can use a level-order traversal approach to print all the nodes at a given level in
a Binary Search Tree (BST).
In this example, a Node class is defined, which has three data members, data left
and right. The printGivenLevel function takes the root of the BST and the level to be
printed as parameters. It starts by checking if the root is None, in which case it
returns. If the level is 1, it prints the current node's data. If the level is greater than 1,
it recursively calls the printGivenLevel function on the left and right children, with a
level decremented by 1.

The getHeight function takes the root of the BST as a parameter and returns the
tree's height. The printLevelOrder function takes the root of the BST as a parameter
and calls the printGivenLevel function for each level of the tree, starting from 1 to the
height of the tree.

Print all leaf nodes:


You can use a depth-first traversal approach to print all the leaf nodes of a Binary
Search Tree (BST).
In this example, a Node class is defined, which has three data members, data left
and right. The printLeafNodes function takes the root of the BST as a parameter and
recursively visits the left and right subtrees of the tree.

The function starts by checking if the root is None, in which case it returns.
Otherwise, it checks if the root node is a leaf node (i.e., it has no left or right children)
by checking if 'root.left' and 'root.right' is None. If the root node is a leaf node, it prints
the data of the root node. Then it recursively calls the printLeafNodes function on the
left and right children.

This process continues until all the tree leaf nodes are visited, and their data is
printed.

Print all non leaf node:


You can use a depth-first traversal approach to print all the non-leaf nodes of a
Binary Search Tree (BST).

In this example, a Node class is defined, which has three data members, data left
and right. The printNonLeafNodes function takes the root of the BST as a parameter
and recursively visits the left and right subtrees of the tree.
The function starts by checking if the root is None, in which case it returns.
Otherwise, it checks if the root node is a non-leaf node (i.e., it has left or right
children) by checking if root.left or root.right is None. If the root node is a non-leaf
node, it prints the data of the root node. Then it recursively calls the
printNonLeafNodes function on the left and right children.

This process continues until all the non-leaf nodes of the tree are visited, and their
data is printed.

Right view of BST:


The right view of a Binary Search Tree (BST) is the set of nodes visible when the
tree is viewed from the right side.

In this example, a Node class is defined, which has three data members, data left
and right. The printRightView function takes the root of the BST, the current level of
the node, and a list of max_level as parameters. The function first checks if the root
is None, in which case it returns.
Otherwise, it checks if the current level is greater than the max_level. If it is, it prints
the data of the root node and updates the max_level. Then it recursively calls the
printRightView function on the right child first, followed by the left child, with the level
incremented by 1.

The rightView function takes the root of the BST as a parameter and calls the
printRightView function with level 1 and max_level set to 0.
This process continues until all the tree nodes are visited, and the correct view of the
tree is printed.

Left view of BST:


The left view of a Binary Search Tree (BST) is the set of nodes visible when the tree
is viewed from the left side.

In this example, a Node class is defined, which has three data members, data left
and right. The printLeftView function takes the root of the BST, the current level of
the node, and a list of max_level as parameters. The function first checks if the root
is None, in which case it returns.
Otherwise, it checks if the current level is greater than the max_level. If it is, it prints
the data of the root node and updates the max_level. Then it recursively calls the
printLeftView function on the left child first, followed by the right child, with the level
incremented by 1.
The leftView function takes the root of the BST as a parameter and calls the
printLeftView function with level 1 and max_level set to 0.

This process continues until all the tree nodes are visited and the left view of the tree
is printed.

Height of BST:
The height of a Binary Search Tree (BST) is the number of levels in the tree.
In this example, a Node class is defined, which has three data members, data left
and right. The height function takes the root of the BST as a parameter and
recursively visits the left and right subtrees of the tree.

The function starts by checking if the root is None, in which case it returns 0.
Otherwise, it finds the height of the left subtree by calling the height function
recursively on the left child and stores it in the variable left_height. Similarly, it finds
the height of the right subtree and keeps it in the variable right_height.

Then it compares the left_height and right_height and returns the maximum of the
two plus 1 (to account for the current level).

This process continues until all the tree nodes are visited and the tree's height is
returned.

Delete a Node of BST:


Deleting a node from a Binary Search Tree (BST) involves a few steps to ensure the
tree remains valid after the deletion.
In this example, a Node class is defined, which has three data members, data left
and right. The deleteNode function takes the root of the BST and the key to be
deleted as parameters. Finally, the findMinNode function is used to find the in-order
successor of the node to be deleted.

The deleteNode function starts by checking if the root is None, in which case it
returns None. Then it checks if the key to be deleted is less than the root data; it
recursively calls the deleteNode function on the left subtree. If the key is greater than
the root data, it recursively calls the deleteNode function on the right subtree.

If the key is equal to the root data, it checks if the root has no left child or no right
child, in which case it can directly delete the root. If the root has both a left and right
child, it finds the in-order successor of the root and replaces the root data with it,
then recursively calls the deleteNode function on the right subtree.
This process continues until the node with the given key is found and deleted, and
the tree is modified accordingly.

Smallest Node of the BST:


Finding the smallest node in a Binary Search Tree (BST) is a simple task that can be
done by traversing the left subtree of the tree until a leaf node is reached.

In this example, a Node class is defined, which has three data members, data left
and right. The findMinNode function takes the root of the BST as a parameter,
traverses the tree's left subtree until it reaches a leaf node. Then, the function returns
the data of the leftmost leaf node.

Total number of nodes in a BST:


Calculating the total number of nodes in a Binary Search Tree (BST) can be done by
traversing the tree and counting the number of nodes.
In this example, a Node class is defined, which has three data members, data left
and right. The countNodes function takes the root of the BST as a parameter, and it
recursively visits the left and right subtrees of the tree. The function starts by
checking if the root is None, in which case it returns 0. Otherwise, it returns the sum
of the number of nodes in the left subtree, the number of nodes in the right subtree,
and 1 (to account for the current node).

This process continues until all the tree nodes are visited and the total number of
nodes is returned.

Delete a BST:
Deleting a Binary Search Tree (BST) involves removing all the nodes of the tree.
This can be done by traversing the tree in a post-order fashion, which visits the left
and right children before the current node and then frees the memory allocated to
the current node.
In this example, a Node class is defined, which has three data members, data left
and right. The deleteTree function takes the root of the BST as a parameter, and it
recursively visits the left and right subtrees of the tree in a post-order fashion. The
function starts by checking if the root is None, in which case it returns. Otherwise, it
recursively calls the deleteTree function on the left and right children of the current
node, and then it prints the current node's data and deletes it using the del keyword.
This process continues until all the tree nodes are visited, and the tree is deleted.

Applications of BST

Binary Search Trees (BSTs) has many practical applications, some of which include:
1. Sorting: BST can sort a large amount of data efficiently.
2. Searching: BST allows fast searching of elements in a large data set.
3. Dictionary operations: BST can implement dictionary operations like adding,
searching, and deleting words.
4. Data compression: BST can be used for data compression by storing only the
differences between consecutive elements rather than the elements
themselves.
5. File systems: BST can locate files quickly on a hard drive.
6. Graph algorithms: BST can be used in graph algorithms such as Dijkstra's
shortest path algorithm.
7. Game development: BST can be used for AI, physics, and collision detection.
8. Database indexing: BST can be used to speed up queries.

BSTs are efficient data structures with many real-world applications due to their time
and space complexity, making them a good choice for many data-driven
applications.

Advantages of Binary Search Tree:

Binary Search Trees (BSTs) have several advantages, some of which include the
following:
1. Fast Search: BSTs offer O(log n) time complexity for searching an element
much faster than a linear search O(n) in an unsorted array or list.
2. Fast Insertion and Deletion: BSTs offer O(log n) time complexity for inserting
and deleting elements faster than O(n) in an unsorted array or list.
3. Sorted order: BSTs maintain the elements in a sorted order which is helpful for
many applications.
4. Space efficient: BSTs use less memory than an unsorted array or list, as they
only store references to the left and right children.
5. Easy to implement: BSTs are easy to implement and understand, which
makes them a good choice for many data-driven applications.

Disadvantages of Binary Search Tree:

Binary Search Trees (BSTs) also have some disadvantages, which include the
following:
1. Unbalanced Trees: If the tree becomes unstable, the time complexity of the
search, insertion and deletion operations can degrade to O(n), which is slower
than O(log n) in a balanced tree.
2. Extra Space: In a worst-case scenario, a BST can degenerate into a linked list
and require extra space to store the left and right pointers.
3. The complexity of balancing: Keeping a tree balanced is a complex task, and
different balancing algorithms have advantages and disadvantages.
4. Extra time for balancing: Maintaining a balanced tree takes spare time and
effort, which can add to the program's complexity.
If you're looking to get into Web Development,
then AlmaBetter is the best place to start your
journey.

Join our Full Stack Web Development Program


and launch your career in tech today!

Link: https://link.almabetter.com/9w63

You might also like