0% found this document useful (0 votes)
10 views

DSA Notes2

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

DSA Notes2

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 51

MEMORY REPRESENTATION

Since C is a structured language, it has some fixed rules for


programming. One of them includes changing the size of an array. An
array is a collection of items stored at contiguous memory locations.

As can be seen, the length (size) of the array above is 9. But what if
there is a requirement to change this length (size)? For example,
• If there is a situation where only 5 elements are needed to be

entered in this array. In this case, the remaining 4 indices are just
wasting memory in this array. So there is a requirement to lessen the
length (size) of the array from 9 to 5.
• Take another situation. In this, there is an array of 9 elements with

all 9 indices filled. But there is a need to enter 3 more elements in


this array. In this case, 3 indices more are required. So the length
(size) of the array needs to be changed from 9 to 12.
This procedure is referred to as Dynamic Memory Allocation in C.
Therefore, C Dynamic Memory Allocation can be defined as a
procedure in which the size of a data structure (like Array) is changed
during the runtime.
C provides some functions to achieve these tasks. There are 4 library
functions provided by C defined under <stdlib.h> header file to
facilitate dynamic memory allocation in C programming. They are:
1. malloc()
2. calloc()
3. free()
4. realloc()
Before learning above functions, let's understand the difference between
static memory allocation and dynamic memory allocation.

static memory allocation dynamic memory allocation

memory is allocated at compile


memory is allocated at run time.
time.

memory can't be increased while memory can be increased while


executing program. executing program.

used in array. used in linked list.

allocates single block of requested


malloc()
memory.

allocates multiple block of


calloc()
requested memory.

reallocates the memory occupied


realloc()
by malloc() or calloc() functions.

frees the dynamically allocated


free()
memory.
C malloc()
The name "malloc" stands for memory allocation.

The malloc() function reserves a block of memory of the specified


number of bytes. And, it returns a pointer of void which can be casted
into pointers of any form.

Syntax of malloc()

ptr = (castType*) malloc(size);

Example ptr = (float*) malloc(100 * sizeof(float));


The above statement allocates 400 bytes of memory. It's because the size
of float is 4 bytes. And, the pointer ptr holds the address of the first byte
in the allocated memory.
The expression results in a NULL pointer if the memory cannot be
allocated.

C calloc()
The name "calloc" stands for contiguous allocation.

The malloc() function allocates memory and leaves the memory


uninitialized, whereas the calloc() function allocates memory and
initializes all bits to zero.

Syntax of calloc()

ptr = (castType*)calloc(n, size);


Example:

ptr = (float*) calloc(25, sizeof(float));

The above statement allocates contiguous space in memory for 25


elements of type float.

C free()
Dynamically allocated memory created with
either calloc() or malloc() doesn't get freed on their own. You must
explicitly use free() to release the space.
Syntax of free()

free(ptr);

This statement frees the space allocated in the memory pointed by ptr.

C realloc()
If the dynamically allocated memory is insufficient or more than
required, you can change the size of previously allocated memory using
the realloc() function.

Syntax of realloc()

ptr = realloc(ptr, x);

Here, ptr is reallocated with a new size x.


EXPRESSION PARSING IN DATA STRUCTURE

An expression is any word or group of words or symbols that generates a


value on evaluation. Parsing expression means analyzing the expression
for its words or symbols depending on a particular criterion. Expression
parsing is a term used in a programming language to evaluate arithmetic
and logical expressions.

The way to write arithmetic expression is known as a notation. An


arithmetic expression can be written in three different but equivalent
notations, i.e., without changing the essence or output of an expression.
These notations are −

• Infix Notation
• Prefix (Polish) Notation
• Postfix (Reverse-Polish) Notation

These notations are named as how they use operator in expression. We


shall learn the same here in this chapter.

The following table briefly tries to show the difference in all three
notations −

Postfix
Sr.No. Infix Notation Prefix Notation
Notation

1 a+b +ab ab+

2 (a + b) ∗ c ∗+abc ab+c∗

3 a ∗ (b + c) ∗a+bc abc+∗

4 a/b+c/d +/ab/cd ab/cd/+


5 (a + b) ∗ (c + d) ∗+ab+cd ab+cd+∗

6 ((a + b) ∗ c) - d -∗+abcd ab+c∗d-

Some operator precedence rules follow:

• Parentheses: expressions inside parentheses are evaluated first


• Exponentiation: exponents are evaluated next
• Multiplication and division: multiplication and division are
evaluated before addition and subtraction
• Addition and subtraction: finally, addition and subtraction are
evaluated last
Infix to Postfix Conversion using Stack in C
LINKED LIST
A linked list is a linear data structure that consists of a series of nodes
connected by pointers (in C or C++) or references (in Java, Python and
JavaScript). Each node contains data and a pointer/reference to the
next node in the list. Unlike arrays, linked lists allow for
efficient insertion or removal of elements from any position in the
list, as the nodes are not stored contiguously in memory.
Linked Lists vs Arrays
Here’s the comparison of Linked List vs Arrays
Linked List:
• Data Structure: Non-contiguous

• Memory Allocation: Typically allocated one by one to individual

elements
• Insertion/Deletion: Efficient

• Access: Sequential

Array:
• Data Structure: Contiguous

• Memory Allocation: Typically allocated to the whole array

• Insertion/Deletion: Inefficient

• Access: Random

Types of Linked List

1. Singly Linked List


2. Doubly Linked List
3. Circular Linked List
4. Circular Doubly Linked List
5. Header Linked List
Operations of Linked Lists:

• Linked List Insertion


• Search an element in a Linked List (Iterative and Recursive)
• Find Length of a Linked List (Iterative and Recursive)
• Reverse a linked list
• Linked List Deletion (Deleting a given key)
• Linked List Deletion (Deleting a key at given position)

Applications of Linked Lists in real world:

• The list of songs in the music player are linked to the previous and
next songs.
• In a web browser, previous and next web page URLs can be linked
through the previous and next buttons (Doubly Linked List)
• In image viewer, the previous and next images can be linked with
the help of the previous and next buttons (Doubly Linked List)
• Circular Linked Lists can be used to implement things in round
manner where we go to every element one by one.
• Linked List are preferred over arrays for implementations of Queue
and Deque data structures because of fast deletions (or insertions)
from the front of the linked lists.

Linked List Applications

• Implementing stacks and queues using linked lists.


• Using linked lists to handle collisions in hash tables.
• Representing graphs using linked lists.
• Allocating and deallocating memory dynamically.
Singly Linked List

A singly linked list is a fundamental data structure in computer


science and programming, it consists of nodes where each node
contains a data field and a reference to the next node in the node.
Operations on Singly Linked List
• Traversal

• Searching

• Length

• Insertion:

o Insert at the beginning

o Insert at the end

o Insert at a specific position

• Deletion:

o Delete from the beginning

o Delete from the end

o Delete a specific node

Traversal in Singly Linked List

Traversal involves visiting each node in the linked list and performing
some operation on the data. A simple traversal function would print or
process the data of each node.
Step-by-step approach:
• Initialize a pointer current to the head of the list.

• Use a while loop to iterate through the list until the current pointer

reaches NULL.
• Inside the loop, print the data of the current node and move the

current pointer to the next node.


Searching in Singly Linked List

Searching in a Singly Linked List refers to the process of looking for a


specific element or value within the elements of the linked list.
Step-by-step approach:
1. Traverse the Linked List starting from the head.
2. Check if the current node’s data matches the target value.
• If a match is found, return true.

3. Otherwise, Move to the next node and repeat steps 2.


4. If the end of the list is reached without finding a match, return false.

Finding Length in Singly Linked List

Finding Length in Singly Linked List refers to the process of


determining the total number of nodes in a singly linked list.
Step-by-step approach:
• Initialize a counter length to 0.

• Start from the head of the list, assign it to current.

• Traverse the list:

o Increment length for each node.

o Move to the next node (current = current->next).

• Return the final value of length.

Insertion in Singly Linked List


Insertion is a fundamental operation in linked lists that involves adding
a new node to the list. There are several scenarios for insertion:
a. Insertion at the Beginning of Singly Linked List:

Step-by-step approach:
• Create a new node with the given value.

• Set the next pointer of the new node to the current head.

• Move the head to point to the new node.

• Return the new head of the linked list.

b. Insertion at the End of Singly Linked List:


To insert a node at the end of the list, traverse the list until the last
node is reached, and then link the new node to the current last node.-

Step-by-step approach:
• Create a new node with the given value.

• Check if the list is empty:

o If it is, make the new node the head and return.

• Traverse the list until the last node is reached.


• Link the new node to the current last node by setting the last node’s
next pointer to the new node.

c. Insertion at a Specific Position of the Singly Linked List:


To insert a node at a specific position, traverse the list to the desired
position, link the new node to the next node, and update the links
accordingly.

We mainly find the node after which we need to insert the new node. If
we encounter a NULL before reaching that node, it means that the
given position is invalid.
Deletion in Singly Linked List

Deletion involves removing a node from the linked list. Similar to


insertion, there are different scenarios for deletion:

a. Deletion at the Beginning of Singly Linked List:


To delete the first node, update the head to point to the second node in
the list.

Steps-by-step approach:
• Check if the head is NULL.

o If it is, return NULL (the list is empty).

• Store the current head node in a temporary variable temp.

• Move the head pointer to the next node.

• Delete the temporary node.

• Return the new head of the linked list.


b. Deletion at the End of Singly Linked List:
To delete the last node, traverse the list until the second-to-last node
and update its next field to None.

Step-by-step approach:
• Check if the head is NULL.

o If it is, return NULL (the list is empty).

• Check if the head’s next is NULL (only one node in the list).

o If true, delete the head and return NULL.

• Traverse the list to find the second last node (second_last).

• Delete the last node (the node after second_last).

• Set the next pointer of the second last node to NULL.

• Return the head of the linked list.

c. Deletion at a Specific Position of Singly Linked List:


To delete a node at a specific position, traverse the list to the desired
position, update the links to bypass the node to be deleted.
Step-by-step approach:
• Check if the list is empty or the position is invalid, return if so.

• If the head needs to be deleted, update the head and delete the node.

• Traverse to the node before the position to be deleted.

• If the position is out of range, return.

• Store the node to be deleted.

• Update the links to bypass the node.

• Delete the stored node.

Doubly Linked List


A doubly linked list is a data structure that consists of a set of nodes,
each of which contains a value and two pointers, one pointing to
the previous node in the list and one pointing to the next node in the
list. This allows for efficient traversal of the list in both directions,
making it suitable for applications where
frequent insertions and deletions are required.
Representation of Doubly Linked List in Data Structure
In a data structure, a doubly linked list is represented using nodes that
have three fields:
1. Data
2. A pointer to the next node (next)
3. A pointer to the previous node (prev)

Node Definition
Here is how a node in a Doubly Linked List is typically represented:

Operations on Doubly Linked List

• Traversal in Doubly Linked List


• Searching in Doubly Linked List
• Finding Length of Doubly Linked List

• Insertion in Doubly Linked List:


o Insertion at the beginning of Doubly Linked List

o Insertion at the end of the Doubly Linked List

o Insertion at a specific position in Doubly Linked List

• Deletion in Doubly Linked List:


o Deletion of a node at the beginning of Doubly Linked List

o Deletion of a node at the end of Doubly Linked List

o Deletion of a node at a specific position in Doubly Linked

List
Let’s go through each of the operations mentioned above, one by one.
Traversal in Doubly Linked List
To Traverse the doubly list, we can use the following steps:
a. Forward Traversal:
• Initialize a pointer to the head of the linked list.

• While the pointer is not null:

o Visit the data at the current node.

o Move the pointer to the next node.

b. Backward Traversal:
• Initialize a pointer to the tail of the linked list.

• While the pointer is not null:

o Visit the data at the current node.

o Move the pointer to the previous node.

Finding Length of Doubly Linked List


To find the length of doubly list, we can use the following steps:
• Start at the head of the list.

• Traverse through the list, counting each node visited.

• Return the total count of nodes as the length of the list.

Insertion at the Beginning in Doubly Linked List

To insert a new node at the beginning of the doubly list, we can use the
following steps:
• Create a new node, say new_node with the given data and set its

previous pointer to null, new_node->prev = NULL.


• Set the next pointer of new_node to current head, new_node->next
= head.
• If the linked list is not empty, update the previous pointer of the

current head to new_node, head->prev = new_node.


• Return new_node as the head of the updated linked list.

Insertion at the End of Doubly Linked List

To insert a new node at the end of the doubly linked list, we can use
the following steps:
• Allocate memory for a new node and assign the provided value to

its data field.


• Initialize the next pointer of the new node to nullptr.

• If the list is empty:

o Set the previous pointer of the new node to nullptr.

o Update the head pointer to point to the new node.

• If the list is not empty:

o Traverse the list starting from the head to reach the last node.

o Set the next pointer of the last node to point to the new node.

o Set the previous pointer of the new node to point to the last

node.

Insertion at a Specific Position in Doubly Linked List

To insert a node at a specific Position in doubly linked list, we can use


the following steps:
To insert a new node at a specific position,
• If position = 1, create a new node and make it the head of the linked

list and return it.


• Otherwise, traverse the list to reach the node at position – 1,

say curr.
• If the position is valid, create a new node with given data,

say new_node.
• Update the next pointer of new node to the next of current node and

prev pointer of new node to current node, new_node->next = curr-


>next and new_node->prev = curr.
• Similarly, update next pointer of current node to the new

node, curr->next = new_node.


• If the new node is not the last node, update prev pointer of new

node’s next to the new node, new_node->next->prev = new_node.

Deletion at the Beginning of Doubly Linked List


To delete a node at the beginning in doubly linked list, we can use the
following steps:
• Check if the list is empty, there is nothing to delete. Return.

• Store the head pointer in a variable, say temp.

• Update the head of linked list to the node next to the current

head, head = head->next.


• If the new head is not NULL, update the previous pointer of new

head to NULL, head->prev = NULL.

Deletion at the End of Doubly Linked List

To delete a node at the end in doubly linked list, we can use the
following steps:
• Check if the doubly linked list is empty. If it is empty, then there is

nothing to delete.
• If the list is not empty, then move to the last node of the doubly

linked list, say curr.


• Update the second-to-last node’s next pointer to NULL, curr-

>prev->next = NULL.
• Free the memory allocated for the node that was deleted.

Deletion at a Specific Position in Doubly Linked List


To delete a node at a specific position in doubly linked list, we can use
the following steps:
• Traverse to the node at the specified position, say curr.

• If the position is valid, adjust the pointers to skip the node to be

deleted.
o If curr is not the head of the linked list, update the next

pointer of the node before curr to point to the node after


curr, curr->prev->next = curr-next.
o If curr is not the last node of the linked list, update the

previous pointer of the node after curr to the node before


curr, curr->next->prev = curr->prev.
• Free the memory allocated for the deleted node.

Applications of Doubly Linked List


• Implementation of undo and redo functionality in text editors.

• Cache implementation where quick insertion and deletion of

elements are required.


• Browser history management to navigate back and forth between

visited pages.
• Music player applications to manage playlists and navigate through

songs efficiently.
• Implementing data structures like Deque (double-ended queue) for

efficient insertion and deletion at both ends.


Circular Linked List
A circular linked list is a special type of linked list where all the
nodes are connected to form a circle. Unlike a regular linked list,
which ends with a node pointing to NULL, the last node in a circular
linked list points back to the first node. This means that you can keep
traversing the list without ever reaching a NULL value.
Types of Circular Linked Lists
We can create a circular linked list from both singly linked
lists and doubly linked lists. So, circular linked list are basically of two
types:
1. Circular Singly Linked List
In Circular Singly Linked List, each node has just one pointer called
the “next” pointer. The next pointer of last node points back to
the first node and this results in forming a circle. In this type of
Linked list we can only move through the list in one direction.

2. Circular Doubly Linked List:


In circular doubly linked list, each node has two
pointers prev and next, similar to doubly linked list. The prev pointer
points to the previous node and the next points to the next node. Here,
in addition to the last node storing the address of the first node,
the first node will also store the address of the last node.
Note: In this article, we will use the circular singly linked list to
explain the working of circular linked lists.

Representation of a Circular Singly Linked List


Let’s take a look on the structure of a circular linked list.

Operations on the Circular Linked list:


We can do some operations on the circular linked list similar to the
singly and doubly linked list which are:

• Insertion
o Insertion at the empty list

o Insertion at the beginning

o Insertion at the end

o Insertion at the given position

• Deletion
o Delete the first node

o Delete the last node

o Delete the node from any position

• Searching
Insertion in the circular linked list:
Insertion is a fundamental operation in linked lists that involves adding
a new node to the list. The only extra step is connecting the last node to
the first one. In the circular linked list mentioned below, we can insert
nodes in four ways:
1. Insertion in an empty List in the circular linked list
To insert a node in empty circular linked list, creates a new node with
the given data, sets its next pointer to point to itself, and updates the
last pointer to reference this new node.

2. Insertion at the beginning in circular linked list


To insert a new node at the beginning of a circular linked list, we first
create the new node and allocate memory for it. If the list is empty
(indicated by the last pointer being NULL), we make the new node
point to itself. If the list already contains nodes then we set the new
node’s next pointer to point to the current head of the list (which is
last->next), and then update the last node’s next pointer to point to the
new node. This maintains the circular structure of the list.
3. Insertion at the end in circular linked list
To insert a new node at the end of a circular linked list, we first create
the new node and allocate memory for it. If the list is empty (mean,
last or tail pointer being NULL), we initialize the list with the new
node and making it point to itself to form a circular structure. If the list
already contains nodes then we set the new node’s next pointer to
point to the current head (which is tail->next), then update the
current tail’s next pointer to point to the new node. Finally, we
update the tail pointer to the new node. This will ensure that the new
node is now the last node in the list while maintaining the circular
linkage.

4. Insertion at specific position in circular linked list


To insert a new node at a specific position in a circular linked list, we
first check if the list is empty. If it is and the position is not 1 then we
print an error message because the position doesn’t exist in the list. If
the position is 1 then we create the new node and make it point to
itself. If the list is not empty, we create the new node and traverse the
list to find the correct insertion point. If the position is 1, we insert the
new node at the beginning by adjusting the pointers accordingly. For
other positions, we traverse through the list until we reach the desired
position and inserting the new node by updating the pointers. If the
new node is inserted at the end, we also update the last pointer to
reference the new node, maintaining the circular structure of the list.
Deletion from a Circular Linked List
Deletion involves removing a node from the linked list. The main
difference is that we need to ensure the list remains circular after the
deletion. We can delete a node in a circular linked list in three ways:

1. Delete the first node in circular linked list


To delete the first node of a circular linked list, we first check if the list is
empty. If it is then we print a message and return NULL. If the list contains
only one node (the head is the same as the last) then we delete that node and set
the last pointer to NULL. If there are multiple nodes then we update the last-
>next pointer to skip the head node and effectively removing it from the list.
We then delete the head node to free the allocated memory. Finally, we return
the updated last pointer, which still points to the last node in the list.

2. Delete a specific node in circular linked list


To delete a specific node from a circular linked list, we first check if
the list is empty. If it is then we print a message and return nullptr. If
the list contains only one node and it matches the key then we delete
that node and set last to nullptr. If the node to be deleted is the first
node then we update the next pointer of the last node to skip the head
node and delete the head. For other nodes, we traverse the list using
two pointers: curr (to find the node) and prev (to keep track of the
previous node). If we find the node with the matching key then we
update the next pointer of prev to skip the curr node and delete it. If
the node is found and it is the last node, we update the last pointer
accordingly. If the node is not found then do nothing and tail or last as
it is. Finally, we return the updated last pointer.

3. Deletion at the end of Circular linked list


To delete the last node in a circular linked list, we first check if the list
is empty. If it is, we print a message and return nullptr. If the list
contains only one node (where the head is the same as the last), we
delete that node and set last to nullptr. For lists with multiple nodes,
we need to traverse the list to find the second last node. We do this by
starting from the head and moving through the list until we reach the
node whose next pointer points to last. Once we find the second last
node then we update its next pointer to point back to the head, this
effectively removing the last node from the list. We then delete the last
node to free up memory and return the updated last pointer, which now
points to the last node.
Searching in Circular Linked list
Searching in a circular linked list is similar to searching in a regular
linked list. We start at a given node and traverse the list until you either
find the target value or return to the starting node. Since the list is
circular, make sure to keep track of where you started to avoid an
infinite loop.
To search for a specific value in a circular linked list, we first check if
the list is empty. If it is then we return false. If the list contains nodes
then we start from the head node (which is the last->next) and traverse
the list. We use a pointer curr to iterate through the nodes until we
reach back to the head. During traversal, if we find a node whose data
matches the given key then we return true to indicating that the value
was found. After the loop, we also check the last node to ensure we
don’t miss it. If the key is not found after traversing the entire list then
we return false.

Advantages of Circular Linked Lists


• In circular linked list, the last node points to the first node. There

are no null references, making traversal easier and reducing the


chances of encountering null pointer exceptions.
• We can traverse the list from any node and return to it without

needing to restart from the head, which is useful in applications


requiring a circular iteration.
• Circular linked lists can easily implement circular queues, where the

last element connects back to the first, allowing for efficient


resource management.
• In a circular linked list, each node has a reference to the next node
in the sequence. Although it doesn’t have a direct reference to the
previous node like a doubly linked list, we can still find the
previous node by traversing the list.

Disadvantages of Circular Linked Lists


• Circular linked lists are more complex to implement than singly

linked lists.
• Traversing a circular linked list without a clear stopping condition

can lead to infinite loops if not handled carefully.


• Debugging can be more challenging due to the circular nature, as

traditional methods of traversing linked lists may not apply.

Applications of Circular Linked Lists


• It is used for time-sharing among different users, typically through

a Round-Robin scheduling mechanism.


• In multiplayer games, a circular linked list can be used to switch

between players. After the last player’s turn, the list cycles back to
the first player.
• Circular linked lists are often used in buffering applications, such as

streaming data, where data is continuously produced and consumed.


• In media players, circular linked lists can manage playlists, this

allowing users to loop through songs continuously.


• Browsers use circular linked lists to manage the cache. This allows

you to navigate back through your browsing history efficiently by


pressing the BACK button.
QUEUE
The Queue in data structure is an ordered, linear sequence of items. It is
a FIFO (First In First Out) data structure, which means that we can insert
an item to the rear end of the queue and remove from the front of the
queue only. A Queue is a sequential data type, unlike an array, in an
array, we can access any of its elements using indexing, but we can only
access the element at the front of the queue at a time
A queue is a linear data structure where elements are stored in the FIFO
(First In First Out) principle where the first element inserted would be
the first element to be accessed. A queue is an Abstract Data Type
(ADT) similar to stack, the thing that makes queue different from stack
is that a queue is open at both its ends. The data is inserted into the
queue through one end and deleted from it using the other end. Queue is
very frequently used in most programming languages.
IIn programming terms, putting items in the queue is called enqueue,
and removing items from the queue is called dequeue.

Basic Operations of Queue


A queue is an object (an abstract data structure - ADT) that allows the
following operations:
• Enqueue: Add an element to the end of the queue
• Dequeue: Remove an element from the front of the queue
• IsEmpty: Check if the queue is empty
• IsFull: Check if the queue is full
• Peek: Get the value of the front of the queue without removing it

Working of Queue
Queue operations work as follows:
• two pointers FRONT and REAR
• FRONT track the first element of the queue
• REAR track the last element of the queue
• initially, set value of FRONT and REAR to -1

Enqueue Operation
• check if the queue is full
• for the first element, set the value of FRONT to 0
• increase the REAR index by 1
• add the new element in the position pointed to by REAR

Dequeue Operation
• check if the queue is empty
• return the value pointed by FRONT
• increase the FRONT index by 1
• for the last element, reset the values of FRONT and REAR to -1
Types of Queue
There are four different types of queue that are listed as follows -

• Simple Queue or Linear Queue


• Circular Queue
• Priority Queue
• Double Ended Queue (or Deque)

• 1. Simple Queue or Linear Queue


• In Linear Queue, an insertion takes place from one end while the
deletion occurs from another end. The end at which the insertion takes
place is known as the rear end, and the end at which the deletion takes
place is known as front end. It strictly follows the FIFO rule.

• The major drawback of using a linear Queue is that insertion is done


only from the rear end. If the first three elements are deleted from the
Queue, we cannot insert more elements even though the space is
available in a Linear Queue. In this case, the linear Queue shows the
overflow condition as the rear is pointing to the last element of the
Queue.

• 2. Circular Queue
In Circular Queue, all the nodes are represented as circular. It is similar
to the linear Queue except that the last element of the queue is connected
to the first element. It is also known as Ring Buffer, as all the ends are
connected to another end.

The main advantage of a circular queue over a simple queue is better


memory utilization. If the last position is full and the first position is
empty, we can insert an element in the first position. This action is not
possible in a simple queue.
2.

Priority Queue
It is a special type of queue in which the elements are arranged based on
the priority. It is a special type of queue data structure in which every
element has a priority associated with it. Suppose some elements occur
with the same priority, they will be arranged according to the FIFO
principle. The representation of priority queue is shown in the below
image -
Insertion in priority queue takes place based on the arrival, while
deletion in the priority queue occurs based on the priority. Priority
queue is mainly used to implement the CPU scheduling algorithms.

Deque (or, Double Ended Queue)


In Deque or Double Ended Queue, insertion and deletion can be done
from both ends of the queue either from the front or rear. It means that
we can insert and delete elements from both front and rear ends of the
queue. Deque can be used as a palindrome checker means that if we read
the string from both ends, then the string would be the same.

Deque can be used both as stack and queue as it allows the insertion
and deletion operations on both ends. Deque can be considered as
stack because stack follows the LIFO (Last In First Out) principle in
which insertion and deletion both can be performed only from one
end. And in deque, it is possible to perform both insertion and
deletion from one end, and Deque does not follow the FIFO
principle
There are two types of deque that are discussed as follows -

o Input restricted deque - As the name implies, in input restricted


queue, insertion operation can be performed at only one end, while
deletion can be performed from both ends.

o Output restricted deque - As the name implies, in output


restricted queue, deletion operation can be performed at only one
end, while insertion can be performed from both ends.
TREE
Other data structures such as arrays, linked list, stack, and queue are
linear data structures that store data sequentially. In order to perform any
operation in a linear data structure, the time complexity increases with
the increase in the data size. But, it is not acceptable in today's
computational world.
A tree is a non-linear abstract data type with a hierarchy-based structure.
It consists of nodes (where the data is stored) that are connected via
links. The tree data structure stems from a single node called a root node
and has subtrees connected to the root.
Tree data structure is a hierarchical structure that is used to represent
and organize data in a way that is easy to navigate and search. It is a
collection of nodes that are connected by edges and has a hierarchical
relationship between the nodes.
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.

The data structure is called a "tree" because it looks like a tree, only
upside down

The Tree data structure can be useful in many cases:

• Hierarchical Data: File systems, organizational models, etc.


• Databases: Used for quick data retrieval.
• Routing Tables: Used for routing data in network algorithms.
• Sorting/Searching: Used for sorting data and searching for data.
• Priority Queues: Priority queue data structures are commonly
implemented using trees, such as binary heaps.

• The first node in a tree is called the root node.

• A link connecting one node to another is called an edge.

• A parent node has links to its child nodes. Another word for a
parent node is internal node.

• A node can have zero, one, or many child nodes.

• A node can only have one parent node.

• Nodes without links to other child nodes are called leaves, or leaf
nodes.

• The tree height is the maximum number of edges from the root
node to a leaf node. The height of the tree above is 2.

• The height of a node is the maximum number of edges between


the node and a leaf node.
• The tree size is the number of nodes in the tree.
Tree Terminologies
Node
A node is an entity that contains a key or value and pointers to its child
nodes.
The last nodes of each path are called leaf nodes or external nodes that
do not contain a link/pointer to child nodes.
The node having at least a child node is called an internal node.
Edge
It is the link between any two nodes.

Root
It is the topmost node of a tree.
Height of a Node
The height of a node is the number of edges from the node to the deepest
leaf (ie. the longest path from the node to a leaf node).

Depth of a Node
The depth of a node is the number of edges from the root to the node.
Height of a Tree
The height of a Tree is the height of the root node or the depth of the
deepest node.
Degree of a Node
The degree of a node is the total number of branches of that node.
Forest
A collection of disjoint trees is called a forest
Basic Terminologies In Tree Data Structure:
• Parent Node: The node which is a predecessor of a node is called

the parent node of that node. {B} is the parent node of {D, E}.

• Child de: 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 Trees in Data Structure


Let’s learn about the different types of trees in data structure:

General Trees

General trees are unordered tree data structures where the root node has
minimum 0 or maximum ‘n’ subtrees.
The General trees have no constraint placed on their hierarchy. The root
node thus acts like the superset of all the other subtrees.

Binary Trees

Binary Trees are general trees in which the root node can only hold up to
maximum 2 subtrees: left subtree and right subtree. Based on the
number of children, binary trees are divided into three types.

Full Binary Tree

• A full binary tree is a binary tree type where every node has either
0 or 2 child nodes.

Complete Binary Tree

• A complete binary tree is a binary tree type where all the leaf nodes
must be on the same level. However, root and internal nodes in a
complete binary tree can either have 0, 1 or 2 child nodes.
Perfect Binary Tree

• A perfect binary tree is a binary tree type where all the leaf nodes
are on the same level and every node except leaf nodes have 2
children.

Binary Search Trees

Binary Search Trees possess all the properties of Binary Trees including
some extra properties of their own, based on some constraints, making
them more efficient than binary trees.

The data in the Binary Search Trees (BST) is always stored in such a
way that the values in the left subtree are always less than the values in
the root node and the values in the right subtree are always greater than
the values in the root node, i.e. left subtree < root node ≤ right subtree.
Balanced Binary Search Trees

Consider a Binary Search Tree with ‘m’ as the height of the left subtree
and ‘n’ as the height of the right subtree. If the value of (m-n) is equal to
0,1 or -1, the tree is said to be a Balanced Binary Search Tree.

The trees are designed in a way that they self-balance once the height
difference exceeds 1. Binary Search Trees use rotations as self-balancing
algorithms. There are four different types of rotations: Left Left, Right
Right, Left Right, Right Left.

There are various types of self-balancing binary search trees −

• AVL Trees
• Red Black Trees
• B Trees
• B+ Trees
• Splay Trees
• Priority Search Trees

You might also like