Understanding Data Structures and ADTs
Understanding Data Structures and ADTs
A data structure is a storage that is used to store and organize data. It is a way of arranging data on a
computer so that it can be accessed and updated efficiently.
A data structure is not only used for organizing the data. It is also used for processing, retrieving, and
storing data. There are different basic and advanced types of data structures that are used in almost
every program or software system that has been developed. So we must have good knowledge of data
structures.
Data structures are an integral part of computers used for the arrangement of data in memory. They are
essential and responsible for organizing, processing, accessing, and storing data efficiently. But this is
not all. Various types of data structures have their own characteristics, features, applications,
advantages, and disadvantages. So how do you identify a data structure that is suitable for a particular
task? What is meant by the term ‘Data Structure’? How many types of data structures are there and
what are they used for?
We already have learned about data structure. Many times, what happens is that people get confused
between data type and data structure. So let’s see a few differences between data type and data
structure to make it clear.
Classification of Data Structure:
Data structure has many different uses in our daily life. There are many different data structures that are
used to solve different mathematical and logical problems. By using data structure, one can organize
and process a very large amount of data in a relatively short period. Let’s look at different data
structures that are used in different situations.
Non-linear data structure: Data structures where data elements are not placed sequentially or linearly
are called non-linear data structures. In a non-linear data structure, we can’t traverse all the elements in
a single run only.
The structure of the data and the synthesis of the algorithm are relative to each other. Data
presentation must be easy to understand so the developer, as well as the user, can make an efficient
implementation of the operation.
Data structures provide an easy way of organizing, retrieving, managing, and storing data.
Now there might be a situation when we need operations for our user-defined data type which have to
be defined. These operations can be defined only as and when we require them. So, in order to simplify
the process of solving problems, we can create data structures along with their operations, and such
data structures that are not in-built are known as Abstract Data Type (ADT).
Abstract Data type (ADT) is a type (or class) for objects whose behavior is defined by a set of values and
a set of operations. The definition of ADT only mentions what operations are to be performed but not
how these operations will be implemented. It does not specify how data will be organized in memory
and what algorithms will be used for implementing the operations. It is called “abstract” because it gives
an implementation-independent view.
The process of providing only the essentials and hiding the details is known as abstraction.
The user of data type does not need to know how that data type is implemented, for example, we have
been using Primitive values like int, float, char data types only with the knowledge that these data type
can operate and be performed on without any idea of how they are implemented.
So a user only needs to know what a data type can do, but not how it will be implemented. Think of ADT
as a black box which hides the inner structure and design of the data type. Now we’ll define three ADTs
namely List ADT, Stack ADT, Queue ADT.
1. List ADT:
The data is generally stored in key sequence in a list which has a head structure consisting of
count, pointers and address of compare function needed to compare the data in the list.
The data node contains the pointer to a data structure and a self-referential pointer which
points to the next node in the list.
The List ADT Functions is given below:
get() – Return an element from the list at any given position.
insert() – Insert an element at any position of the list.
remove() – Remove the first occurrence of any element from a non-empty list.
removeAt() – Remove the element at a specified location from a non-empty list.
replace() – Replace an element at any position by another element.
size() – Return the number of elements in the list.
isEmpty() – Return true if the list is empty, otherwise return false.
isFull() – Return true if the list is full, otherwise return false.
2. Queue ADT:
The queue abstract data type (ADT) follows the basic design of the stack abstract data type.
Each node contains a void pointer to the data and the link pointer to the next element in the
queue. The program’s responsibility is to allocate memory for storing the data.
enqueue() – Insert an element at the end of the queue.
dequeue() – Remove and return the first element of the queue, if the queue is not empty.
peek() – Return the element of the queue without removing it, if the queue is not empty.
size() – Return the number of elements in the queue.
isEmpty() – Return true if the queue is empty, otherwise return false.
isFull() – Return true if the queue is full, otherwise return false.
Features of ADT:
Abstract data types (ADTs) are a way of encapsulating data and operations on that data into a
single unit. Some of the key features of ADTs include:
Abstraction: The user does not need to know the implementation of the data structure only
essentials are provided.
Better Conceptualization: ADT gives us a better conceptualization of the real world.
Robust: The program is robust and has the ability to catch errors.
Encapsulation: ADTs hide the internal details of the data and provide a public interface for users
to interact with the data. This allows for easier maintenance and modification of the data
structure.
Data Abstraction: ADTs provide a level of abstraction from the implementation details of the
data. Users only need to know the operations that can be performed on the data, not how those
operations are implemented.
Data Structure Independence: ADTs can be implemented using different data structures, such
as arrays or linked lists, without affecting the functionality of the ADT.
Information Hiding: ADTs can protect the integrity of the data by allowing access only to
authorized users and operations. This helps prevent errors and misuse of the data.
Modularity: ADTs can be combined with other ADTs to form larger, more complex data
structures. This allows for greater flexibility and modularity in programming.
Overall, ADTs provide a powerful tool for organizing and manipulating data in a structured and
efficient manner.
Abstract data types (ADTs) have several advantages and disadvantages that should be
considered when deciding to use them in software development. Here are some of the main
advantages and disadvantages of using ADTs:
Advantages of ADT:
Encapsulation: ADTs provide a way to encapsulate data and operations into a single unit,
making it easier to manage and modify the data structure.
Abstraction: ADTs allow users to work with data structures without having to know the
implementation details, which can simplify programming and reduce errors.
Data Structure Independence: ADTs can be implemented using different data structures, which
can make it easier to adapt to changing needs and requirements.
Information Hiding: ADTs can protect the integrity of data by controlling access and preventing
unauthorized modifications.
Modularity: ADTs can be combined with other ADTs to form more complex data structures,
which can increase flexibility and modularity in programming.
Disadvantages of ADT:
Overhead: Implementing ADTs can add overhead in terms of memory and processing, which can
affect performance.
Complexity: ADTs can be complex to implement, especially for large and complex data
structures.
Learning Curve: Using ADTs requires knowledge of their implementation and usage, which can
take time and effort to learn.
Limited Flexibility: Some ADTs may be limited in their functionality or may not be suitable for all
types of data structures.
Cost: Implementing ADTs may require additional resources and investment, which can increase
the cost of development.
Overall, the advantages of ADTs often outweigh the disadvantages, and they are widely used in
software development to manage and manipulate data in a structured and efficient way.
However, it is important to consider the specific needs and requirements of a project when
deciding whether to use ADTs.
From these definitions, we can clearly see that the definitions do not specify how these ADTs
will be represented and how the operations will be carried out. There can be different ways to
implement an ADT, for example, the List ADT can be implemented using arrays, or singly linked
list or doubly linked list. Similarly, stack ADT and Queue ADT can be implemented using arrays or
linked lists.
This article is contributed by Anuj Chauhan. If you like GeeksforGeeks and would like to
contribute, you can also write an article using [Link] or mail your article to
review-team@[Link]. See your article appearing on the GeeksforGeeks main page
and help other Geeks.
What basic operations do we want our lists to support? Our common intuition about lists tells us
that a list should be able to grow and shrink in size as we insert and remove elements. We
should be able to insert and remove elements from anywhere in the list. We should be able to
gain access to any element’s value, either to read it or to change it. Finally, we should be able to
know the size of the list, and to iterate through the elements in the list – i.e., the list should be a
Collection.
Now we can define the ADT for a list object in terms of a set of operations on that object. We
will use an interface to formally define the list ADT. List defines the member functions that any
list implementation inheriting from it must support, along with their parameters and return
types.
True to the notion of an ADT, an interface does not specify how operations are implemented.
Two complete implementations are presented later (array-based lists and linked lists), both of
which use the same list ADT to define their operations. But they are considerably different in
approaches and in their space/time tradeoffs.
The code below presents our list ADT. The comments given with each member function describe
what it is intended to do. However, an explanation of the basic design should help make this
clearer. There are four main operations we want to support:
add(i,x) to add (insert) an element x, at position i, thus increasing the size of the list
remove(i) to remove the element at position i, thus decreasing the size of the list.
Linked list Data Structure:
A linked list is a linear data structure that includes a series of connected nodes. Here, each node
stores the data and the address of the next node. For example,
You have to start somewhere, so we give the address of the first node a special name called
HEAD. Also, the last node in the linked list can be identified because its next portion points to
NULL.
Linked lists can be of multiple types: singly, doubly, and circular linked list. In this article, we will
focus on the singly linked list.
There are various linked list operations that allow us to perform different actions on linked lists. For
example, the insertion operation adds a new element to the linked list.
Here's a list of basic linked list operations that we will cover in this article.
Before you learn about linked list operations in detail, make sure to know about Linked List first.
next pointer of the last node is NULL, so if the next current node is NULL, we have reached the end of
structure as below:
Traverse a Linked List
Displaying the contents of a linked list is very simple. We keep moving the temp node to the
next one and display its contents.
When temp is NULL, we know that we have reached the end of the linked list so we get out of
the while loop.
You can add elements to either the beginning, middle or end of the linked list.
1. Insert at the beginning
Store data
Store data
You can delete either from the beginning, end or from a particular position.
You can search an element on a linked list using a loop using the following steps. We are finding item on
a linked list.
Make head as the current node.
Run a loop until the current node is NULL because the last element points to NULL.
In each iteration, check if the key of the node is equal to item. If it the key matches the item,
We will use a simple sorting algorithm, Bubble Sort, to sort the elements of a linked list in ascending
order below.
1. Make the head as the current node and create another node index for later use.
6. Check if the data of the current node is greater than the next node. If it is greater,
swap current and index.
Program python:
# Create a node
class Node:
def __init__(self, data):
[Link] = data
[Link] = None
class LinkedList:
def __init__(self):
[Link] = None
new_node.next = [Link]
[Link] = new_node
if prev_node is None:
print("The given previous node must inLinkedList.")
return
new_node = Node(new_data)
new_node.next = prev_node.next
prev_node.next = new_node
if [Link] is None:
[Link] = new_node
return
last = [Link]
while ([Link]):
last = [Link]
[Link] = new_node
# Deleting a node
def deleteNode(self, position):
if [Link] is None:
return
temp = [Link]
if position == 0:
[Link] = [Link]
temp = None
return
if [Link] is None:
return
next = [Link]
[Link] = None
[Link] = next
# Search an element
def search(self, key):
current = [Link]
current = [Link]
return False
# Sort the linked list
def sortLinkedList(self, head):
current = head
index = Node(None)
if head is None:
return
else:
while current is not None:
# index points to the node next to current
index = [Link]
index = [Link]
current = [Link]
if __name__ == '__main__':
llist = LinkedList()
[Link](1)
[Link](2)
[Link](3)
[Link](4)
[Link]([Link], 5)
print('linked list:')
[Link]()
print()
item_to_find = 3
if [Link](item_to_find):
print(str(item_to_find) + " is found")
else:
print(str(item_to_find) + " is not found")
[Link]([Link])
print("Sorted List: ")
[Link]()
Consider a polynomial P(x) = 7x2 + 15x3 - 2 x2 + 9. Here 7, 15, -2, and 9 are
the coefficients, and 4,3,2,0 are the exponents of the terms in the
polynomial. On representing this polynomial using a linked list, we have
Observe that the number of nodes equals the number of terms in the
polynomial. So we have 4 nodes. Moreover, the terms are stored to decrease
exponents in the linked list. Such representation of polynomial using linked
lists makes the operations like subtraction, addition, multiplication, etc., on
polynomial very easy.
Addition of Polynomials:
To add two polynomials, we traverse the list P and Q. We take corresponding terms of the list P
and Q and compare their exponents. If the two exponents are equal, the coefficients are added
to create a new coefficient. If the new coefficient is equal to 0, then the term is dropped, and if
it is not zero, it is inserted at the end of the new linked list containing the resulting polynomial. If
one of the exponents is larger than the other, the corresponding term is immediately placed
into the new linked list, and the term with the smaller exponent is held to be compared with the
next term from the other list. If one list ends before the other, the rest of the terms of the
longer list is inserted at the end of the new linked list containing the resulting polynomial.
Q (x) = 5x3 + 4 x2 - 5
1. Traverse the two lists P and Q and examine all the nodes.
2. We compare the exponents of the corresponding terms of two
polynomials. The first term of polynomials P and Q contain exponents 4
and 3, respectively. Since the exponent of the first term of the
polynomial P is greater than the other polynomial Q, the term having a
larger exponent is inserted into the new list. The new list initially looks
as shown below:
We then compare the exponent of the next term of the list P with the
exponents of the present term of list Q. Since the two exponents are equal,
so their coefficients are added and appended to the new list as follows:
Then we move to the next term of P and Q lists and compare their exponents.
Since exponents of both these terms are equal and after addition of their
coefficients, we get 0, so the term is dropped, and no node is appended to
the new list after this,
Moving to the next term of the two lists, P and Q, we find that the
corresponding terms have the same exponents equal to 0. We add their
coefficients and append them to the new list for the resulting polynomial as
shown below:
Stack is a liner ADT, with restrictions in inserting and deleting elements from the same end. It’s
like making a pile of plates in which the first plate will be the last plate to be taken. It works on
the principle of LIFO (last-in, last-out). It stores only one type of data
The entering and retrieving of data is also called push and pop operation in a stack. There are
different operations possible in a stack like reversing a stack using recursion, Sorting, Deleting
the middle element of a stack, etc.
In Stack ADT Implementation instead of data being stored in each node, the pointer to data is
stored.
The program allocates memory for the data and address is passed to the stack ADT.
The head node and the data nodes are encapsulated in the ADT. The calling function can only
see the pointer to the stack.
The stack head structure also contains a pointer to top and count of number of entries currently
in stack.
push() – Insert an element at one end of the stack called top.
pop() – Remove and return the element at the top of the stack, if it is not empty.
peek() – Return the element at the top of the stack without removing it, if the stack is not
empty.
size() – Return the number of elements in the stack.
isEmpty() – Return true if the stack is empty, otherwise return false.
isFull() – Return true if the stack is full, otherwise return false.
Insertion: push()
push() is an operation that inserts elements into the stack. The following is an algorithm that
describes the push() operation in a simpler way.
Algorithm
1 − Checks if the stack is full.
2 − If the stack is full, produces an error and exit.
3 − If the stack is not full, increments top to point next empty space.
4 − Adds data element to the stack location, where top is pointing.
5 − Returns success.
pop() is a data manipulation operation which removes elements from the stack. The following
pseudo code describes the pop() operation in a simpler way.
Algorithm
1 − Checks if the stack is empty.
2 − If the stack is empty, produces an error and exit.
3 − If the stack is not empty, accesses the data element at which top is pointing.
4 − Decreases the value of top by 1.
5 − Returns success.
class Stack:
def __init__(self):
[Link] = []
def __str__(self):
return str([Link])
def push(self, data):
if data not in [Link]:
[Link](data)
return True
else:
return False
def remove(self):
if len([Link]) <= 0:
return ("No element in the Stack")
else:
return [Link]()
stk = Stack()
[Link](1)
[Link](2)
[Link](3)
[Link](4)
[Link](5)
print("Stack Elements:")
print(stk)
print("----Deletion operation in stack----")
p = [Link]()
print("The popped element is: " + str(p))
print("Updated Stack:")
print(stk)
peek()
The peek() is an operation retrieves the topmost element within the stack, without
deleting it. This operation is used to check the status of the stack with the help of the top
pointer.
Algorithm
1. START
2. return the element at the top of the stack
3. END
class Stack:
def __init__(self):
[Link] = []
def __str__(self):
return str([Link])
def push(self, data):
if data not in [Link]:
[Link](data)
return True
else:
return False
# Use peek to look at the top of the stack
def peek(self):
return [Link][-1]
stk = Stack()
[Link](1)
[Link](2)
[Link](3)
[Link](4)
[Link](5)
print("Stack Elements:")
print(stk)
print("topmost element: ",[Link]())
isFull()
isFull() operation checks whether the stack is full. This operation is used to check the
status of the stack with the help of top pointer.
Algorithm
1. START
2. If the size of the stack is equal to the top position of the stack, the stack is
full. Return 1.
3. Otherwise, return 0.
4. END
isEmpty()
The isEmpty() operation verifies whether the stack is empty. This operation is used to
check the status of the stack with the help of top pointer.
Algorithm
1. START
2. If the top value is -1, the stack is empty. Return 1.
3. Otherwise, return 0.
4. END
Python Implementation
class Stack:
def __init__(self):
[Link] = []
def add(self, data):
if data not in [Link]:
[Link](data)
return True
else:
return False
Characteristics of a Stack:
Stack has various different characteristics which are as follows:
Stack is used in many different algorithms like Tower of Hanoi, tree traversal, recursion, etc.
Stack is implemented through an array or linked list.
It follows the Last In First Out operation i.e., an element that is inserted first will pop in last and
vice versa.
The insertion and deletion are performed at one end i.e. from the top of the stack.
In stack, if the allocated space for the stack is full, and still anyone attempts to add more
elements, it will lead to stack overflow.
Applications of Stack:
Different applications of Stack are as follows:
The stack data structure is used in the evaluation and conversion of arithmetic expressions.
Stack is used in Recursion.
It is used for parenthesis checking.
While reversing a string, the stack is used as well.
Stack is used in memory management.
It is also used for processing function calls.
The stack is used to convert expressions from infix to postfix.
The stack is used to perform undo as well as redo operations in word processors.
The stack is used in virtual machines like JVM.
The stack is used in the media players. Useful to play the next and previous song.
The stack is used in recursion operations.
Real-Life Applications of Stack:
Real life example of a stack is the layer of eating plates arranged one above the other. When you
remove a plate from the pile, you can take the plate to the top of the pile. But this is exactly the
plate that was added most recently to the pile. If you want the plate at the bottom of the pile,
you must remove all the plates on top of it to reach it.
Browsers use stack data structures to keep track of previously visited sites.
Call log in mobile also uses stack data structure.
Advantages of Stack:
Stacks are simple data structures with a well-defined set of operations, which makes them easy
to understand and use.
Stacks are efficient for adding and removing elements, as these operations have a time
complexity of O(1).
In order to reverse the order of elements we use the stack data structure.
Stacks can be used to implement undo/redo functions in applications.
Drawbacks of Stack:
Restriction of size in Stack is a drawback and if they are full, you cannot add any more elements
to the stack.
Stacks do not provide fast access to elements other than the top element.
Stacks do not support efficient searching, as you have to pop elements one by one until you find
the element you are looking for.
Application of Stack Data Structure:
Function calls and recursion: When a function is called, the current state of the program is
pushed onto the stack. When the function returns, the state is popped from the stack to resume
the previous function’s execution.
Undo/Redo operations: The undo-redo feature in various applications uses stacks to keep track
of the previous actions. Each time an action is performed, it is pushed onto the stack. To undo
the action, the top element of the stack is popped, and the reverse operation is performed.
Expression evaluation: Stack data structure is used to evaluate expressions in infix, postfix, and
prefix notations. Operators and operands are pushed onto the stack, and operations are
performed based on the stack’s top elements.
Browser history: Web browsers use stacks to keep track of the web pages you visit. Each time
you visit a new page, the URL is pushed onto the stack, and when you hit the back button, the
previous URL is popped from the stack.
Balanced Parentheses: Stack data structure is used to check if parentheses are balanced or not.
An opening parenthesis is pushed onto the stack, and a closing parenthesis is popped from the
stack. If the stack is empty at the end of the expression, the parentheses are balanced.
Backtracking Algorithms: The backtracking algorithm uses stacks to keep track of the states of
the problem-solving process. The current state is pushed onto the stack, and when the
algorithm backtracks, the previous state is popped from the stack.
Application of Stack in real life:
CD/DVD stand.
Stack of books in a book shop.
Call center systems.
Undo and Redo mechanism in text editors.
The history of a web browser is stored in the form of a stack.
Call logs, E-mails, and Google photos in any gallery are also stored in form of a stack.
YouTube downloads and Notifications are also shown in LIFO format(the latest appears first ).
Allocation of memory by an operating system while executing a process.
Advantages of Stack:
Easy implementation: Stack data structure is easy to implement using arrays or linked lists, and
its operations are simple to understand and implement.
Efficient memory utilization: Stack uses a contiguous block of memory, making it more efficient
in memory utilization as compared to other data structures.
Fast access time: Stack data structure provides fast access time for adding and removing
elements as the elements are added and removed from the top of the stack.
Helps in function calls: Stack data structure is used to store function calls and their states, which
helps in the efficient implementation of recursive function calls.
Supports backtracking: Stack data structure supports backtracking algorithms, which are used in
problem-solving to explore all possible solutions by storing the previous states.
Used in Compiler Design: Stack data structure is used in compiler design for parsing and syntax
analysis of programming languages.
Enables undo/redo operations: Stack data structure is used to enable undo and redo operations
in various applications like text editors, graphic design tools, and software development
environments.
Disadvantages of Stack:
Limited capacity: Stack data structure has a limited capacity as it can only hold a fixed number
of elements. If the stack becomes full, adding new elements may result in stack overflow,
leading to the loss of data.
No random access: Stack data structure does not allow for random access to its elements, and it
only allows for adding and removing elements from the top of the stack. To access an element in
the middle of the stack, all the elements above it must be removed.
Memory management: Stack data structure uses a contiguous block of memory, which can
result in memory fragmentation if elements are added and removed frequently.
Not suitable for certain applications: Stack data structure is not suitable for applications that
require accessing elements in the middle of the stack, like searching or sorting algorithms.
Stack overflow and underflow: Stack data structure can result in stack overflow if too many
elements are pushed onto the stack, and it can result in stack underflow if too many elements
are popped from the stack.
Recursive function calls limitations: While stack data structure supports recursive function calls,
too many recursive function calls can lead to stack overflow, resulting in the termination of the
program.
Infix Notation
Prefix Notation
Postfix Notation
Infix Notation
The infix notation is a convenient way of writing an expression in which each operator is placed
between the operands. Infix expressions can be parenthesized or unparenthesized depending
upon the problem requirement.
All these expressions are in infix notation because the operator comes
between the operands. Example: A + B, (C - D) etc.
Prefix Notation
The prefix notation places the operator before the operands. This notation was introduced by
the Polish mathematician and hence often referred to as polish notation.
All these expressions are in prefix notation because the operator comes before the operands.
Postfix Notation
The postfix notation places the operator after the operands. This notation is just the reverse of
Polish notation and also known as Reverse Polish notation.
All these expressions are in postfix notation because the operator comes after the operands
Prefix to Postfix Conversion
Prefix: An expression is called the prefix expression if the operator appears in the expression
before the operands. Simply of the form (operator operand1 operand2).
Example : *+AB-CD (Infix : (A+B) * (C-D) )
Postfix: An expression is called the postfix expression if the operator appears in the expression
after the operands. Simply of the form (operand1 operand2 operator).
Example : AB+CD-* (Infix : (A+B * (C-D) )
Given a Prefix expression, convert it into a Postfix expression.
Conversion of Prefix expression directly to Postfix without going through the process of
converting them first to Infix and then to Postfix is much better in terms of computation and
better understanding the expression (Computers evaluate using Postfix expression).
Examples:
# Example Input
s = "*-A/BC-/AKL"
# if token is operator
if i in operators:
# else if operand
else:
[Link](i)
What is a Queue?
Queue is a linear data structure in which the insertion and deletion operations are performed at
two different ends. In a queue data structure, adding and removing elements are performed at
two different positions. The insertion is performed at one end and deletion is performed at
another end. In a queue data structure, the insertion operation is performed at a position which
is known as 'rear' and the deletion operation is performed at a position which is known as
'front'. In queue data structure, the insertion and deletion operations are performed based on
FIFO (First In First Out) principle.
In a queue data structure, the insertion operation is performed using a function called
"enQueue()" and deletion operation is performed using a function called "deQueue()".
Operations on a Queue
The following operations are performed on a queue data structure...
Queue data structure can be implemented in two ways. They are as follows...
1. Using Array
2. Using Linked List
When a queue is implemented using an array, that queue can organize an only limited number
of elements. When a queue is implemented using a linked list, that queue can organize an
unlimited number of elements.
Representation of Queues
Similar to the stack ADT, a queue ADT can also be implemented using arrays, linked
lists, or pointers. As a small example in this tutorial, we implement queues using a one-
dimensional array.
Basic Operations
Queue operations also include initialization of a queue, usage and permanently deleting
the data from the memory.
The most fundamental operations in the queue ADT include: enqueue(), dequeue(),
peek(), isFull(), isEmpty(). These are all built-in operations to carry out data
manipulation and to check the status of the queue.
Queue uses two pointers − front and rear. The front pointer accesses the data from the
front end (helping in enqueueing) while the rear pointer accesses data from the rear end
(helping in dequeuing).
The enqueue() is a data manipulation operation that is used to insert elements into the stack.
The following algorithm describes the enqueue() operation in a simpler way.
Algorithm
1 − START
4 − If the queue is not full, increment rear pointer to point the next empty space.
5 − Add data element to the queue location, where the rear is pointing.
6 − return success.
7 – END
class Queue:
def __init__(self):
[Link] = list()
def __str__(self):
return str([Link])
def addtoqueue(self,data):
q = Queue()
[Link]("36")
[Link]("24")
[Link]("48")
[Link]("12")
[Link]("66")
print("Queue:")
print(q)
The dequeue() is a data manipulation operation that is used to remove elements from the stack.
The following algorithm describes the dequeue() operation in a simpler way.
Algorithm
1 – START
4 − If the queue is not empty, access the data where front is pointing.
6 − Return success.
7 – END
class Queue:
def __init__(self):
[Link] = list()
def __str__(self):
return str([Link])
def addtoqueue(self,data):
q = Queue()
[Link]("36")
[Link]("24")
[Link]("48")
[Link]("12")
[Link]("66")
print("Queue:")
print(q)
The peek() is an operation which is used to retrieve the frontmost element in the queue,
without deleting it. This operation is used to check the status of the queue with the help of the
pointer.
Algorithm
1 – START
3 – END
class Queue:
def __init__(self):
[Link] = list()
def __str__(self):
return str([Link])
def addtoqueue(self,data):
q = Queue()
[Link]("36")
[Link]("24")
[Link]("48")
[Link]("12")
[Link]("66")
print("Queue:")
print(q)
Algorithm
1 – START
2 – If the count of queue elements equals the queue size, return true
4 – END
The isEmpty() operation
The isEmpty() operation verifies whether the queue is empty. This operation is used to check the
status of the queue with the help of top pointer.
Algorithm
1 – START
4 – END
Queue Implementation:
# Queue implementation in Python
class Queue:
def __init__(self):
[Link] = []
# Add an element
def enqueue(self, item):
[Link](item)
# Remove an element
def dequeue(self):
if len([Link]) < 1:
return None
return [Link](0)
def size(self):
return len([Link])
q = Queue()
[Link](1)
[Link](2)
[Link](3)
[Link](4)
[Link](5)
[Link]()
[Link]()
Applications of Queue:
Multi programming: Multi programming means when multiple programs are
running in the main memory. It is essential to organize these multiple
programs and these multiple programs are organized as queues.
Create a class QNode with data members integer data and QNode* next
A parameterized constructor that takes an integer x value as a
parameter and sets data equal to x and next as NULL
Create a class Queue with data members QNode front and rear
Enqueue Operation with parameter x:
Initialize QNode* temp with data = x
If the rear is set to NULL then set the front and rear to temp and
return(Base Case)
Else set rear next to temp and then move rear to temp
Dequeue Operation:
If the front is set to NULL return(Base Case)
Initialize QNode temp with front and set front to its next
If the front is equal to NULL then set the rear to NULL
Delete temp from the memory
class Node:
[Link] = data
[Link] = None
class Queue:
def __init__(self):
def isEmpty(self):
temp = Node(item)
if [Link] == None:
return
[Link] = temp
[Link] = temp
def DeQueue(self):
if [Link]():
return
temp = [Link]
[Link] = [Link]
if([Link] == None):
[Link] = None
# Driver Code
if __name__ == '__main__':
q = Queue()
[Link](10)
[Link](20)
[Link]()
[Link]()
[Link](30)
[Link](40)
[Link](50)
[Link]()
Output:
Types of Queue
There are four different types of queue that are listed as follows -
o Simple Queue or Linear Queue
o Circular Queue
o Priority Queue
o Double Ended Queue (or Deque)
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.
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 representation of circular queue is shown in the below
image –
The drawback that occurs in a linear queue is overcome by using the circular
queue. If the empty space is available in a circular queue, the new element
can be added in an empty space by simply incrementing the value of rear.
The main advantage of using the circular queue is better memory utilization.
The operations are performed based on FIFO (First In First Out) principle.
It is also called ‘Ring Buffer’.
Python 3
class CircularQueue():
# constructor
def __init__(self, size): # initializing the class
[Link] = size
def dequeue(self):
if ([Link] == -1): # condition for empty queue
print ("Queue is Empty\n")
def display(self):
else:
print ("Elements in Circular Queue are:",
end = " ")
for i in range([Link], [Link]):
print([Link][i], end = " ")
for i in range(0, [Link] + 1):
print([Link][i], end = " ")
print ()
# Driver Code
ob = CircularQueue(5)
[Link](14)
[Link](22)
[Link](13)
[Link](-6)
[Link]()
print ("Deleted value = ", [Link]())
print ("Deleted value = ", [Link]())
[Link]()
[Link](9)
[Link](20)
[Link](5)
[Link]()
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.
There are two types of priority queue that are discussed as follows -
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.
Operations on deque
Example 1: Appending Items Efficiently
append():- This function is used to insert the value in its argument to the
right end of the deque.
appendleft():- This function is used to insert the value in its argument to the
left end of the deque.
Python3
# initializing deque
de = [Link]([1, 2, 3])
[Link](4)
print(de)
[Link](6)
# printing modified deque
print(de)
Output:
pop():- This function is used to delete an argument from the right end of the
deque.
popleft():- This function is used to delete an argument from the left end of
the deque.
Python3
import collections
# initializing deque
de = [Link]([6, 1, 2, 3, 4])
[Link]()
print(de)
[Link]()
print(de)
Output:
What are Scheduling Queues?
The processes waiting for a device are placed in Device Queues. There are unique
device queues which are available for every I/O device.
First place a new process in the Ready queue and then it waits in the ready queue till it
is selected for execution.
Once the process is assigned to the CPU and is executing, any one of the following
events occur −
The process issue an I/O request, and then placed in the I/O queue.
The process may create a new sub process and wait for termination.
The process may be removed forcibly from the CPU, which is an interrupt, and it
is put back in the ready queue.
In the first two cases, the process switches from the waiting state to the ready state, and
then puts it back in the ready queue. A process continues this cycle till it terminates, at
which time it is removed from all queues and has its PCB and resources deallocated.
Types of Schedulers
There are three types of schedulers available which are as follows −
Program:
# Python3 program for the above approach
import heapq
def printJobScheduling(arr):
n = len(arr)
# Driver's Code
if __name__ == '__main__':
arr = [['a', 2, 100], # Job Array
['b', 1, 19],
['c', 2, 27],
['d', 1, 25],
['e', 3, 15]]
# Function Call
printJobScheduling(arr)