Unit-2 Complete Notes

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 22

UNIT-II

LINEAR DATA STRUCTURES AND APPLICATIONS


Linear Data Structures are a type of data structure in computer science where data
elements are arranged sequentially or linearly. Each element has previous and next
adjacent, except for the first and last elements.

Characteristics of Linear Data Structure:


 Sequential Organization: In linear data structures, data elements are arranged
sequentially, one after the other. Each element has a unique predecessor (except for the
first element) and a unique successor (except for the last element)
 Order Preservation: The order in which elements are added to the data structure is
preserved. This means that the first element added will be the first one to be accessed or
removed, and the last element added will be the last one to be accessed or removed.
 Fixed or Dynamic Size: Linear data structures can have either fixed or dynamic sizes.
Arrays typically have a fixed size when they are created, while other structures like
linked lists, stacks, and queues can dynamically grow or shrink as elements are added or
removed.
 Efficient Access: Accessing elements within a linear data structure is typically
efficient. For example, arrays offer constant-time access to elements using their index.

Linear data structures are commonly used for organising and manipulating data in a
sequential fashion. Some of the most common linear data structures include:

 Arrays: A collection of elements stored in contiguous memory locations.


 Linked Lists: A collection of nodes, each containing an element and a reference to the
next node.
 Stacks: A collection of elements with Last-In-First-Out (LIFO) order.
 Queues: A collection of elements with First-In-First-Out (FIFO) order.

Applications of Linear Data Structure


 Efficient data access: Elements can be easily accessed by their position in the
sequence.
 Dynamic sizing: Linear data structures can dynamically adjust their size as elements
are added or removed.
 Ease of implementation: Linear data structures can be easily implemented using arrays
or linked lists.
 Versatility: Linear data structures can be used in various applications, such as
searching, sorting, and manipulation of data.
 Simple algorithms: Many algorithms used in linear data structures are simple and
straightforward.

ABSTRACT DATA TYPES

An Abstract Data Type (ADT) is a programming concept that defines data structure,
without specifying the implementation details. In other words, it is a blueprint for
creating a data structure that defines the behaviour without specifying how it is
implemented.
 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. ADT 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

Vies of list

 The data is generally stored in key sequence in a 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. Stack ADT

View of stack

 Stacks: A collection of elements with Last-In-First-Out (LIFO) order.


 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.
3. Queue ADT

View of Queue

 Queues: A collection of elements with First-In-First-Out (FIFO) order.


 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.
 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.

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:
 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:
 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.

Stack

A stack is an abstract data type (ADT) which is used to store data in a linear(sequential) way.
A stack only has a single end (which is stack's top) through which we can insert or delete data
from it.

There are many different stack operations which can be applied on a stack.

Operations in stack:

push(), pop(), peek(), isEmpty(), isFull(), size()


Stack Representation

A stack follows a Last in, First out principle (LIFO). This means the data inserted in the stack
last will be first to be removed. Also, the data is inserted or deleted through the stack top.

Given below is the stack representation to show how data is inserted and deleted in a stack.

Stack Operations

There are various stack operations that are applicable on a stack. Stack operations are
generally used to extract information and data from a stack data structure.

Some of the stack operations are given below.

1. push()

Push is a function in stack definition which is used to insert data at the stack's top.

2. pop()

Pop is a function in the stack definition which is used to remove data from the stack's top.
3. topElement() / peek()

TopElement / Peek is a function in the stack which is used to extract the element present at
the stack top.

4. isEmpty()

isEmpty is a boolean function in stack definition which is used to check whether the stack is
empty or not. It returns true if the stack is empty. Otherwise, it returns false.

5. isFull()

isFull is a function which is used to check whether the stack has reached its maximum limit
of insertion of data or not i.e. if 'maxLimit' is the maximum number of elements that can be
stored in the stack and if there are exactly maxLimit number of elements present in the stack
currently, then the function isFull() returns true. Otherwise, if the number of elements present
in the stack currently are less than 'maxLimit', then isFull() returns false.

6. size()

Size is a function in stack definition which is used to find out the number of elements that are
present inside the stack.

 A stack is a data structure which is used to store data in a linear fashion.


 Stack follows a Last in, First out (LIFO) principle i.e. the data which is inserted most
recently in the stack will be deleted first from the stack.
 There are many stack operations that can be performed on a stack. Some of them
are push(), pop(), peek(), isEmpty(), isFull(), size().
Array implementation of Stack

In array implementation, the stack is formed by using the array. All the operations regarding
the stack are performed using arrays.

Push operation

Adding an element into the top of the stack is referred to as push operation. Push operation
involves following two steps.

1. Increment the variable Top so that it can now refer to the next memory location.
2. Add element at the position of incremented top. This is referred to as adding new
element at the top of the stack.

Stack is overflown when we try to insert an element into a completely filled stack therefore,
our main function must always avoid stack overflow condition.

Algorithm for push operation of stack

Algorithm for push():

begin
if stack is full
return
endif
else
increment top
stack[top] assign value
end else
end procedure

Pop Operation(Deletion of an element from a stack)

Deletion of an element from the top of the stack is called pop operation. The value of the
variable top will be incremented by 1 whenever an item is deleted from the stack. The top
most element of the stack is stored in an another variable and then the top is decremented by
1. the operation returns the deleted value that was stored in another variable as the result.

The underflow condition occurs when we try to delete an element from an already empty
stack.
Algorithm for pop of stack:

begin
if stack is empty
return
endif
else
store value of stack[top]
decrement top
return value
end else
end procedure

Peek operation (Visiting each element of the stack)

Peek operation involves returning the element which is present at the top of the stack without
deleting it. Underflow condition can occur if we try to return the top element in an already
empty stack.

Algorithm for peek operation of stack:

PEEK (STACK, TOP)

Begin
if top = -1 then stack empty
item = stack[top]
return item
End
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.

WORST TIME SPACE


OPERATION COMPLEXITY COMPLEXITY
Push O(N) O(1)
WORST TIME SPACE
OPERATION COMPLEXITY COMPLEXITY
Pop O(1) O(1)
Peek O(1) O(1)

Queue:

1. A queue can be defined as an ordered list which enables insert operations to be performed
at one end called REAR and delete operations to be performed at another end
called FRONT.

2. Queue is referred to be as First In First Out list.

3. For example, people waiting in line for a rail ticket form a queue.

Applications of Queue

Due to the fact that queue performs actions on first in first out basis which is quite fair for the
ordering of actions. There are various applications of queues discussed as below.

1. Queues are used as buffers in most of the applications like MP3 media player, CD
player, etc.
2. Queue are used to maintain the play list in media players in order to add and remove
the songs from the play-list.
3. Queues are used in operating systems for handling interrupts.
4. Queues are widely used as waiting lists for a single shared resource like printer, disk,
CPU.
5. Queues are used in asynchronous transfer of data for eg. pipes, file IO, sockets.

Queue

Queue is the data structure that is similar to the queue in the real world. A queue is a data
structure in which whatever comes first will go out first, and it follows the FIFO (First-In-
First-Out) policy. Queue can also be defined as the list or collection in which the insertion is
done from one end known as the rear end or the tail of the queue, whereas the deletion is
done from another end known as the front end or the head of the queue.
The real-world example of a queue is the ticket queue outside a cinema hall, where the person
who enters first in the queue gets the ticket first, and the last person enters in the queue gets
the ticket at last. Similar approach is followed in the queue in data structure.

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)

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.

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 -

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

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.
Operations performed on queue

The fundamental operations that can be performed on queue are listed as follows -

o Enqueue: The Enqueue operation is used to insert the element at the rear end of the
queue. It returns void.
o Dequeue: It performs the deletion from the front-end of the queue. It also returns the
element which has been removed from the front-end. It returns an integer value.
o Peek: This is the third operation that returns the element, which is pointed by the
front pointer in the queue but does not delete it.
o Queue overflow (isfull): It shows the overflow condition when the queue is
completely full.
o Queue underflow (isempty): It shows the underflow condition when the Queue is
empty, i.e., no elements are in the Queue.

Array representation of Queue

We can easily represent queue by using linear arrays. There are two variables i.e. front and
rear, that are implemented in the case of every queue. Front and rear variables point to the
position from where insertions and deletions are performed in a queue. Initially, the value of
front and queue is -1 which represents an empty queue. Array representation of a queue
containing 5 elements along with the respective values of front and rear, is shown in the
following figure.
The above figure shows the queue of characters forming the English word "HELLO". Since,
No deletion is performed in the queue till now, therefore the value of front remains -1 .
However, the value of rear increases by one every time an insertion is performed in the
queue. After inserting an element into the queue shown in the above figure, the queue will
look something like following. The value of rear will become 5 while the value of front
remains same.

Queue Deletion of Elements

After deleting an element, the value of front will increase from -1 to 0. however, the queue
will look something like following.
Algorithm to insert any element in a queue

Check if the queue is already full by comparing rear to max - 1. if so, then return an overflow
error.

If the item is to be inserted as the first element in the list, in that case set the value of front
and rear to 0 and insert the element at the rear end.

Otherwise keep increasing the value of rear and insert each element one by one having rear as
the index.

Algorithm to insert element in queue


Algorithm to delete an element from the queue

Circular Queue

Why was the concept of the circular queue introduced?

There was one limitation in the array implementation of Queue. If the rear reaches to the end
position of the Queue then there might be possibility that some vacant spaces are left in the
beginning which cannot be utilized. So, to overcome such limitations, the concept of the
circular queue was introduced.

Operations on Circular Queue

The following are the operations that can be performed on a circular queue:
o Front: It is used to get the front element from the Queue.
o Rear: It is used to get the rear element from the Queue.
o enQueue(value): This function is used to insert the new value in the Queue. The new
element is always inserted from the rear end.
o deQueue(): This function deletes an element from the Queue. The deletion in a
Queue always takes place from the front end.

Applications of Circular Queue

The circular Queue can be used in the following scenarios:

o Memory management: The circular queue provides memory management. As we


have already seen that in linear queue, the memory is not managed very efficiently.
But in case of a circular queue, the memory is managed efficiently by placing the
elements in a location which is unused.
o CPU Scheduling: The operating system also uses the circular queue to insert the
processes and then execute them.
o Traffic system: In a computer-control traffic system, traffic light is one of the best
examples of the circular queue. Each light of traffic light gets ON one by one after
every jinterval of time. Like red light gets ON for one minute then yellow light for
one minute and then green light. After green light, the red light gets ON.

Enqueue operation
The steps of enqueue operation are given below:

o First, we will check whether the Queue is full or not.


o Initially the front and rear are set to -1. When we insert the first element in a Queue,
front and rear both are set to 0.
o When we insert a new element, the rear gets incremented, i.e., rear=rear+1.

Scenarios for inserting an element

There are two scenarios in which queue are not full:

o If rear!= max-1, then rear will be incremented to mod(maxsize) and the new value
will be inserted at the rear end of the queue.
o If front!= 0 and rear = max-1, it means that queue is not full, then set the value of
rear to 0 and insert the new element there.
There are two cases in which the element cannot be inserted:

o When front ==0 && rear = max-1, which means that front is at the first position of
the Queue and rear is at the last position of the Queue.
o front== rear + 1;

Algorithm to insert an element in a circular queue

Step1:IF(REAR+1)%MAX=FRONT
Write "OVERFLOW"
Goto step4
[End OF IF]
Step 2:IF FRONT = -1 and REAR = -1
SET FRONT = REAR = 0
ELSE IF REAR = MAX - 1 and FRONT ! = 0
SET REAR = 0
ELSE
SET REAR = (REAR + 1) % MAX
[END OF IF]
Step 3:SET QUEUE[REAR] = VAL
Step 4:EXIT

Dequeue Operation

The steps of dequeue operation are given below:

o First, we check whether the Queue is empty or not. If the queue is empty, we cannot
perform the dequeue operation.
o When the element is deleted, the value of front gets decremented by 1.
o If there is only one element left which is to be deleted, then the front and rear are reset
to -1.

Algorithm to delete an element from the circular queue

Step 1: IF FRONT =-1


Write "UNDERFLOW"
Goto Step 4
[END of IF]
Step 2: SET VAL = QUEUE[FRONT]
Step 3: IF FRONT = REAR
SET FRONT = REAR = -1
ELSE IF FRONT = MAX -1
SET FRONT = 0
ELSE
SET FRONT = FRONT + 1
[END of IF]
[END OF IF]

Step 4: EXIT

Let's understand the enqueue and dequeue operation through the diagrammatic
representation.
Priority queue

A priority queue is an abstract data type that behaves similarly to the normal queue except
that each element has some priority, i.e., the element with the highest priority would come
first in a priority queue. The priority of the elements in a priority queue will determine the
order in which elements are removed from the priority queue.

The priority queue supports only comparable elements, which means that the elements are
either arranged in an ascending or descending order.
For example, suppose we have some values like 1, 3, 4, 8, 14, 22 inserted in a priority queue
with an ordering imposed on the values is from least to the greatest. Therefore, the 1 number
would be having the highest priority while 22 will be having the lowest priority.

Characteristics of a Priority queue


A priority queue is an extension of a queue that contains the following characteristics:

o Every element in a priority queue has some priority associated with it.
o An element with the higher priority will be deleted before the deletion of the lesser
priority.
o If two elements in a priority queue have the same priority, they will be arranged using
the FIFO principle.

Let's understand the priority queue through an example.

We have a priority queue that contains the following values:

1, 3, 4, 8, 14, 22

All the values are arranged in ascending order. Now, we will observe how the priority queue
will look after performing the following operations:

o Dequeue (): This function will remove the highest priority element from the priority
queue. In the above priority queue, the '1' element has the highest priority, so it will be
removed from the priority queue.
o Enqueue(2): This function will insert '2' element in a priority queue. As 2 is the
smallest element among all the numbers so it will obtain the highest priority.
o Dequeue (): It will remove '2' element from the priority queue as it has the highest
priority queue.
o Enqueue (5): It will insert 5 element after 4 as 5 is larger than 4 and lesser than 8, so
it will obtain the third highest priority in a priority queue.

There are two types of priority queue:

o Ascending order priority queue: In ascending order priority queue, a lower priority
number is given as a higher priority in a priority. For example, we take the numbers
from 1 to 5 arranged in an ascending order like 1,2,3,4,5; therefore, the smallest
number, i.e., 1 is given as the highest priority in a priority queue.

o Descending order priority queue: In descending order priority queue, a higher


priority number is given as a higher priority in a priority. For example, we take the
numbers from 1 to 5 arranged in descending order like 5, 4, 3, 2, 1; therefore, the
largest number, i.e., 5 is given as the highest priority in a priority queue.

Operations in a Priority Queue


A priority queue supports the following operations:
 enqueue(): It inserts new data into the queue.
 Dequeue (): It removes the data with the highest priority from the queue.
 Peek ()/top(): It returns the element having the highest priority in the queue.

Algorithm for priority queue enqueue operation


 Step:1 To enqueue a new element, increment the size by 1. If size is less than maxSize,
then initialize the arr[index] with the value
 Step:2: then initialize the Priority of the new element with the value
 Step:3 If size exceeds the maxSize, then return.
Algorithm for priority queue dequeue operation

 Step 1:It removes the element with the highest priority. So, define two variables index
and highestPriority and iterate over the entire array elements. The variable index stores
the index of the element having the highest priority so far, and the variable
highestPriority stores the priority of arr[index].
Step2:At the end of the iteration, since we need to delete the element with the highest
priority, so left shift all the elements after the position “index” by one position and
finally decrement size by 1.

Algorithm or pseudo code for priority queue peek operation

 Step 1: It just returns the element with the highest priority. So, define two variables
index and highestPriority and iterate over the entire array elements.

 Step2: The variable index stores the index of the element having the highest priority so
far and the variable highestPriority stores the priority of arr[index].

Time Complexity for priority queue


 Enqueue - O(1), as we just append the new element to the end of the array.
 Dequeue - O(n), first we find the index of the highest priority element which takes O(n)
time and then we shift the elements after the peek element to the left which also takes
O(n) time. Hence, total time complexity becomes O(n).
 Peek - O(n), as to find the element with the highest priority we need to iterate over all
the elements in the array so time complexity becomes O(n).

Space Complexity
O(n), where n is the maximum number of elements that can be stored in the priority queue.

Recursion
The process in which a function calls itself is called recursion. Using a recursive algorithm,
certain problems can be solved quite easily.

Recursive Algorithm
A recursive algorithm is a problem-solving approach that involves breaking down a problem
into smaller and smaller sub-problems until the sub-problems become simple enough to be
solved directly. The solution to the original problem is then obtained by combining the
solutions to the smaller sub-problems.
A key feature of a recursive algorithm is that it calls itself with a smaller version of the
problem as its input. The algorithm uses a base case to terminate the recursion when the
problem is small enough that it can be solved directly without further recursion.

For example, consider the factorial function. The factorial of a non-negative integer n
(denoted as n!) is the product of all positive integers from 1 to n. This function is solved as
follows:

factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)

Steps for implementing recursion in a function are as follows:

Step 1- Start with defining a recursive function along with all necessary parameters.

Step 2- Determine a base condition. In that base condition, either it will return a specific
value or end the recursive function.

Step 3- If the base case condition is not met, it will break down the large complex problem
into subproblems to reduce its size.

Step 4- The same function recursively calls itself. If there are multiple recursive calls made,
then they are combined to solve the original problem.

Step 5- Defining the termination condition is essential so that it does not create an infinite
recursion.

You might also like