non_linear_dsa
non_linear_dsa
Data structures where data elements are not arranged sequentially or linearly are
called non-linear data structures. In a non-linear data structure, single level is not
involved. Therefore, we can’t traverse all the elements in single run only. Non-
linear data structures are not easy to implement in comparison to linear data
structure. It utilizes computer memory efficiently in comparison to a linear data
structure. Its examples are trees and graphs.
The data items are arranged in sequential The data items are arranged in non-
order, one after the other. sequential order (hierarchical manner).
It can be traversed on a single run. That is, if It requires multiple runs. That is, if we start
we start from the first element, we can from the first element it might not be
traverse all the elements sequentially in a possible to traverse all the elements in a
single pass. single pass.
Tree
Tree Data Structure is a non-linear data structure in which a collection of
elements known as nodes are connected to each other via edges such that there
exists exactly one path between any two nodes.
Tree data structure is a hierarchical structure that is used to represent and organize
data in the form of parent child relationship. The following are some real world
situations which are naturally a tree.
The topmost node of the tree is called the root, and the nodes below it are called
the child nodes. Each node can have multiple child nodes, and these child nodes
can also have their own child nodes, forming a recursive structure.
Basic Terminologies In Tree Data Structure:
Parent Node: The node which is an immediate predecessor of a node is
called the parent node of that node. {B} is the parent node of {D, E}.
Child Node: The node which is the immediate successor of a node is called
the child node of that node. Examples: {D, E} are the child nodes of {B}.
Root Node: The topmost node of a tree or the node which does not have any
parent node is called the root node. {A} is the root node of the tree. A non-
empty tree must contain exactly one root node and exactly one path from the
root to all other nodes of the tree.
Leaf Node or External Node: The nodes which do not have any child
nodes are called leaf nodes. {I, J, K, F, G, H} are the leaf nodes of the tree.
Ancestor of a Node: Any predecessor nodes on the path of the root to that
node are called Ancestors of that node. {A,B} are the ancestor nodes of the
node {E}
Descendant: A node x is a descendant of another node y if and only if y is
an ancestor of x.
Sibling: Children of the same parent node are called siblings. {D,E} are
called siblings.
Level of a node: The count of edges on the path from the root node to that
node. The root node has level 0.
Internal node: A node with at least one child is called Internal Node.
Neighbour of a Node: Parent or child nodes of that node are called
neighbors of that node.
Subtree: Any node of the tree along with its descendant.
Types of TREE
The following are the different types of tree data structures:
Binary Tree.
Binary Search Tree (BST)
AVL Tree.
B-Tree.
Binary Tree
Types of Binary Tree:
Binary Tree consists of following types based on the number of children:
1. Full Binary Tree
A full binary tree is a binary tree with either zero or two child nodes for
each node.
All the levels except the last level are completely full.
An AVL tree defined as a self-balancing Binary Search Tree (BST) where the
difference between heights of left and right subtrees for any node cannot be more
than one.
(B) AVL Tree
An AVL tree defined as a self-balancing Binary Search Tree (BST) where the
difference between heights of left and right subtrees for any node cannot be more
than one.
The difference between the heights of the left subtree and the right subtree for any
node is known as the balance factor of the node.
(C) B-Tree
A B-tree is a self-balancing tree where all the leaf nodes are at the same level
which allows for efficient searching, insertion and deletion of records. Because of
all the leaf nodes being on the same level, the access time of data is fixed
regardless of the size of the data set.
(D) Binary Search Tree
A Binary Search Tree (or BST) is a data structure used in computer science for
organizing and storing data in a sorted manner. Each node in a Binary Search
Tree has at most two children, a left child and a right child, with the left child
containing values less than the parent node and the right child containing values
greater than the parent node.
This hierarchical structure allows for efficient searching, insertion,
and deletion operations on the data stored in the tree.
Advantages of Binary search tree
o Searching an element in the Binary search tree is easy as we always have a
hint that which subtree has the desired element.
o As compared to array and linked lists, insertion and deletion operations are
faster in BST.
Creation of Binary Search Tree
Now, let's see the creation of binary search tree using an example.
Suppose the data elements are - 45, 15, 79, 90, 10, 55, 12, 20, 50
o First, we have to insert 45 into the tree as the root of the tree.
o Then, read the next element; if it is smaller than the root node, insert it as the
root of the left subtree, and move to the next element.
o Otherwise, if the element is larger than the root node, then insert it as the
root of the right subtree.
Now, let's see the process of creating the Binary search tree using the given data
element. The process of creating the BST is shown below -
Step 1 - Insert 45.
Step2:
Step3:
Now, let's see the algorithm to search an element in the Binary search tree.
Algorithm to search an element in Binary search tree
1. Search (root, item)
2. Step 1 - if (item = root → data) or (root = NULL)
3. return root
4. else if (item < root → data)
5. return Search(root → left, item)
6. else
7. return Search(root → right, item)
8. END if
9. Step 2 - END
(ii) Insertion
A new key in BST is always inserted at the leaf. To insert an element in BST, we
have to start searching from the root node; if the node to be inserted is less than the
root node, then search for an empty location in the left subtree. Else, search for the
empty location in the right subtree and insert the data.
Now, let's see the process of inserting a node into BST using an example.
(iii)Deletion in Binary Search tree
In a binary search tree, we must delete a node from the tree by keeping in mind
that the property of BST is not violated. To delete a node from BST, there are three
possible situations occur -
o The node to be deleted is the leaf node, or,
o The node to be deleted has only one child, and,
o The node to be deleted has two children
We will understand the situations listed above in detail.
When the node to be deleted is the leaf node
It is the simplest case to delete a node in BST. Here, we have to replace the leaf
node with NULL and simply free the allocated space.
We can see the process to delete a leaf node from BST in the below image. In
below image, suppose we have to delete node 90, as the node to be deleted is a leaf
node, so it will be replaced with NULL, and the allocated space will free.
A vertex, also called a node, is a point or an object in the Graph, and an edge is
used to connect two vertices with each other.
Graphs are non-linear because the data structure allows us to have different paths
to get from one vertex to another, unlike with linear data structures like Arrays or
Linked Lists.
Graphs are used to represent and solve problems where the data consists of objects
and relationships between them, such as:
Social Networks: Each person is a vertex, and relationships (like friendships)
are the edges. Algorithms can suggest potential friends.
Maps and Navigation: Locations, like a town or bus stops, are stored as
vertices, and roads are stored as edges. Algorithms can find the shortest
route between two locations when stored as a Graph.
Internet: Can be represented as a Graph, with web pages as vertices and
hyperlinks as edges.
Biology: Graphs can model systems like neural networks or the spread of
diseases.
Graph Properties
Use the animation below to get an understanding of the different Graph properties,
and how these properties can be combined.
Weighted
Connected
Directed
Cyclic
Loop
A weighted Graph is a Graph where the edges have values. The weight value of an
edge can represent things like distance, capacity, time, or probability.
A connected Graph is when all the vertices are connected through edges somehow.
A Graph that is not connected, is a Graph with isolated (disjoint) subgraphs, or
single isolated vertices.
A directed Graph, also known as a digraph, is when the edges between the vertex
pairs have a direction. The direction of an edge can represent things like hierarchy
or flow.
A cyclic Graph is defined differently depending on whether it is directed or not:
A directed cyclic Graph is when you can follow a path along the directed
edges that goes in circles. Removing the directed edge from F to G in the
animation above makes the directed Graph not cyclic anymore.
An undirected cyclic Graph is when you can come back to the same vertex
you started at without using the same edge more than once. The undirected
Graph above is cyclic because we can start and end up in vertes C without
using the same edge twice.
A loop, also called a self-loop, is an edge that begins and ends on the same vertex.
A loop is a cycle that only consists of one edge. By adding the loop on vertex A in
the animation above, the Graph becomes cyclic.
Graph Representations
A Graph representation tells us how a Graph is stored in memory.
Different Graph representations can:
take up more or less space.
be faster or slower to search or manipulate.
be better suited depending on what type of Graph we have (weighted,
directed, etc.), and what we want to do with the Graph.
be easier to understand and implement than others.
Graph representations store information about which vertices are adjacent, and
how the edges between the vertices are. Graph representations are slightly different
if the edges are directed or weighted.
Two vertices are adjacent, or neighbors, if there is an edge between them.
The adjacency matrix above represents an undirected Graph, so the values '1' only
tells us where the edges are. Also, the values in the adjacency matrix is
symmetrical because the edges go both ways (undirected Graph).
Adjacency List Graph Representation
In case we have a 'sparse' Graph with many vertices, we can save space by using
an Adjacency List compared to using an Adjacency Matrix, because an Adjacency
Matrix would reserve a lot of memory on empty Array elements for edges that
don't exist.
A 'sparse' Graph is a Graph where each vertex only has edges to a small portion of
the other vertices in the Graph.
An Adjacency List has an array that contains all the vertices in the Graph, and each
vertex has a Linked List (or Array) with the vertex's edges.
Graph Traversal
(i)BFS algorithm
Breadth-first search(BFS) is a graph traversal algorithm that starts traversing the
graph from the root node and explores all the neighboring nodes. Then, it selects
the nearest node and explores all the unexplored nodes. While using BFS for
traversal, any node in the graph can be considered as the root node.
Algorithm
The steps involved in the BFS algorithm to explore a graph are given as follows -
Step 1: SET STATUS = 1 (ready state) for each node in G
Step 2: Enqueue the starting node A and set its STATUS = 2 (waiting state)
Step 3: Repeat Steps 4 and 5 until QUEUE is empty
Step 4: Dequeue a node N. Process it and set its STATUS = 3 (processed state).
Step 5: Enqueue all the neighbours of N that are in the ready state (whose
STATUS = 1) and set
their STATUS = 2
Advertisement
(waiting state)
[END OF LOOP]
Step 6: EXIT
Example of BFS algorithm
Now, let's understand the working of BFS algorithm by using an example. In the
example given below, there is a directed graph having 7 vertices.
In the above graph, minimum path 'P' can be found by using the BFS that will start
from Node A and end at Node E. The algorithm uses two queues, namely QUEUE1
and QUEUE2. QUEUE1 holds all the nodes that are to be processed, while
QUEUE2 holds all the nodes that are processed and deleted from QUEUE1.
Now, let's start examining the graph starting from Node A.
Step 1 - First, add A to queue1 and NULL to queue2.
Advertisement
1. QUEUE1 = {A}
2. QUEUE2 = {NULL}
Step 2 - Now, delete node A from queue1 and add it into queue2. Insert all
neighbors of node A to queue1.
1. QUEUE1 = {B, D}
2. QUEUE2 = {A}
Step 3 - Now, delete node B from queue1 and add it into queue2. Insert all
neighbors of node B to queue1.
1. QUEUE1 = {D, C, F}
2. QUEUE2 = {A, B}
Step 4 - Now, delete node D from queue1 and add it into queue2. Insert all
neighbors of node D to queue1. The only neighbor of Node D is F since it is
already inserted, so it will not be inserted again.
Advertisement
1. QUEUE1 = {C, F}
2. QUEUE2 = {A, B, D}
Step 5 - Delete node C from queue1 and add it into queue2. Insert all neighbors of
node C to queue1.
1. QUEUE1 = {F, E, G}
2. QUEUE2 = {A, B, D, C}
Step 5 - Delete node F from queue1 and add it into queue2. Insert all neighbors of
node F to queue1. Since all the neighbors of node F are already present, we will
not insert them again.
1. QUEUE1 = {E, G}
2. QUEUE2 = {A, B, D, C, F}
Step 6 - Delete node E from queue1. Since all of its neighbors have already been
added, so we will not insert them again. Now, all the nodes are visited, and the
target node E is encountered into queue2.
1. QUEUE1 = {G}
2. QUEUE2 = {A, B, D, C, F, E}
(ii)DFS (Depth First Search) algorithm
It is a recursive algorithm to search all the vertices of a tree data structure or a
graph. The depth-first search (DFS) algorithm starts with the initial node of graph
G and goes deeper until we find the goal node or the node with no children.
Algorithm
Step 1: SET STATUS = 1 (ready state) for each node in G
Step 2: Push the starting node A on the stack and set its STATUS = 2 (waiting
state)
Step 3: Repeat Steps 4 and 5 until STACK is empty
Step 4: Pop the top node N. Process it and set its STATUS = 3 (processed state)
Step 5: Push on the stack all the neighbors of N that are in the ready state (whose
STATUS = 1) and set their STATUS = 2 (waiting state)
[END OF LOOP]
Step 6: EXIT
Pseudocode
1. DFS(G,v) ( v is the vertex where the search starts )
2. Stack S := {}; ( start with an empty stack )
3. for each vertex u, set visited[u] := false;
4. push S, v;
5. while (S is not empty) do
6. u := pop S;
7. if (not visited[u]) then
8. visited[u] := true;
9. for each unvisited neighbour w of uu
10. push S, w;
11. end if
12. end while
13. END DFS()
Example of DFS algorithm
Now, let's understand the working of the DFS algorithm by using an example. In
the example given below, there is a directed graph having 7 vertices.