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

DSA Minor

Uploaded by

saitejakasani091
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
26 views

DSA Minor

Uploaded by

saitejakasani091
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 28

Data Structures and its applications

Difference Between List and Array in Python

List Array

List is used to collect items that usually An array is also a vital component that collects
consist of elements of multiple data types. several items of the same data type.

List cannot manage arithmetic operations. Array can manage arithmetic operations.

It consists of elements that belong to the It consists of elements that belong to the same
different data types. data type.

When it comes to flexibility, the list is When it comes to flexibility, the array is not
perfect as it allows easy modification of suitable as it does not allow easy modification of
data. data.

It consumes a larger memory. It consumes less memory than a list.

In a list, the complete list can be accessed In an array, a loop is mandatory to access the
without any specific looping. components of the array.

It favors a shorter sequence of data. It favors a longer sequence of data.

Differences between Data type and Data Structure :

Data type Data structure

Data type in general refers to type of the Data structures are user-defined, hence the
value a variable holds , its size and in some type of data to hold depends on the user
cases its range. requirement
A Data type is one of the forms of a A Data structure is a collection of data of same
variable to which the value can be assigned or different data types. This collection of data
of a given type only. This value can be used can be represented using an object and can be
throughout the program. used throughout the program.

The implementation of data type is known The implementation of data structure is known
as an abstract implementation. as a concrete implementation.

It can hold value but not data i.e. multiple It can hold multiple types of data within a single
values . Therefore, we can say that it is object.
data-less.

In case of data type, a value can be In the case of data structure, some operations
assigned directly to the variables. are used to assign the data to the data
structure object.

`There is no problem in the time When we deal with a data structure object,
complexity. time complexity plays an important role.

The examples of data type are int, float, The examples of data structure are stack,
char. queue, tree, graph.
Difference between Linear and Non-linear Data Structures:

S.NO Linear Data Structure Non-linear Data Structure

In a linear data structure, data elements


In a non-linear data structure, data
are arranged in a linear order where each
1. elements are attached in hierarchically
and every element is attached to its
manner.
previous and next adjacent.

In linear data structure, single level is Whereas in non-linear data structure,


2.
involved. multiple levels are involved.

Its implementation is easy in comparison While its implementation is complex in


3.
to non-linear data structure. comparison to linear data structure.

While in non-linear data structure, data


In linear data structure, data elements can
4. elements can’t be traversed in a single run
be traversed in a single run only.
only.

While in a non-linear data structure,


In a linear data structure, memory is not
5. memory is utilized in an efficient way.
utilized in an efficient way.

Its examples are: array, stack, queue,


6. While its examples are: trees and graphs.
linked list, etc.

Applications of linear data structures are Applications of non-linear data structures


7. mainly in application software are in Artificial Intelligence and image
development. processing.

Non-linear data structures are useful for


representing complex relationships and
Linear data structures are useful for simple
8. data hierarchies, such as in social
data storage and manipulation.
networks, file systems, or computer
networks.

Performance is usually good for simple


operations like adding or removing at the Performance can vary depending on the
9. ends, but slower for operations like structure and the operation, but can be
searching or removing elements in the optimized for specific operations.
middle.

Linear Search

What is Search?
Search is a process of finding a value in a list of values. In other words, searching is the process
of locating given value position in a list of values.

Linear Search Algorithm (Sequential Search Algorithm)


 Linear search algorithm finds a given element in a list of elements with O(n) time
complexity where n is total number of elements in the list.
 The linear search is the simplest search where we try to find an element K from
the list L. We have to compare K with each and every item in the list.
 There is two possible situations:
 The array is ordered from lowest to highest.
 The array is unordered.
 If the array is sorted then the search is called an ordered linear search. If a
search is performed on unordered array then it is called an unordered linear search.
Unordered Linear Search
 This search process starts comparing search element with the first element in the list.
 If both are matched then result is element found otherwise search element is compared
with the next element in the list.
 Repeat the same until search element is compared with the last element in the list, if
that last element also doesn't match, then the result is "Element not found in the list".
 That means, the search element is compared with element by element in the list.

Unordered Linear search is implemented using following steps...

Step 1 - Read the search element from the user.


Step 2 - Compare the search element with the first element in the list.
Step 3 - If both are matched, then display "Given element is found!!!" and terminate the
function
Step 4 - If both are not matched, then compare search element with the next element in
the list.
Step 5 - Repeat steps 3 and 4 until search element is compared with last element in the
list.
Step 6 - If last element in the list also doesn't match, then display "Element is not
found!!!" and terminate the function.

Example
Consider the following list of elements and the element to be searched...
Unordered Linear Search
import array as k
a=k.array(‘i’)
n=int(input("Number of elements in array:"))
for i in range(0,n):
l=int(input())
a.append(l)
e=int(input("Enter the element to be searched:"))
pos=0;
for i in range(0,n):
if (a[i]==e):
pos=i+1
break
if(pos==0):
print("Element not found")
else:
print("Element found")
print("Its first occurrence is at the position",pos)

Time Complexity
Time Complexity
Time Complexity
Case (Successful
(Unsuccessful Search)
Search)

Best Case O(1) O(n)

Average Case O(n) O(n)

Worst Case O(n) O(n)

o Best Case Complexity - In Linear search, best case occurs when the element we are finding is at the
first position of the array. The best-case time complexity of linear search is O(1).
o Average Case Complexity - The average case time complexity of linear search is O(n).
o Worst Case Complexity - In Linear search, the worst case occurs when the element we are looking is
present at the end of the array. The worst-case in linear search could be when the target element is not
present in the given array, and we have to traverse the entire array. The worst -case time complexity of
linear search is O(n).

Ordered Linear Search


 If the elements of the array are in order and if we want to search the element , then
algorithm goes through each item in the array as long as :

 Searching element is greater than element which we are traversing, that is, searching
element is greater than any element in the array keep moving.
 When searching element is equal to traversing element at position i , then we have
found the element successfully and return the position.
Program:
import array as k
a=k.array(‘i’)
n=int(input("Number of elements in array:"))
print(" Enter ", n , " elements in sorted order")
for i in range(0,n):
l=int(input())
a.append(l)
e=int(input("Enter the element want to search:"))
pos=0
for i in range(0,n):
if(a[i]==e):
pos=i+1
break
elif(e<a[i]):
break
if(pos==0):
print("Element not found")
else:
print("Element found")
print("Its first occurrence is at the position",pos)

Time Complexity:
The best case time complexity is O(1) while the worst case time complexity is O(n)

Binary Search

Binary search algorithm finds a given element in a list of elements with O(log n) time
complexity where n is total number of elements in the list.
It uses Divide and Conquer rule.
The binary search algorithm can be used with only a sorted list of elements. That means
the binary search is used only with a list of elements that are already arranged in an
order.
The binary search can not be used for a list of elements arranged in random order.
Binary search is implemented using following steps...

This search process starts comparing the search element with the middle element in the
list.

If both are matched, then the result is "element found". Otherwise, we check whether
the search element is smaller or larger than the middle element in the list.

If the search element is smaller, then we repeat the same process for the left sublist of
the middle element.

If the search element is larger, then we repeat the same process for the right sublist of
the middle element.

We repeat this process until we find the search element in the list or until we left with a
sub list of only one element.

And if that element also doesn't match with the search element, then the result
is "Element not found in the list".

Example
Consider the following list of elements and the element to be searched...
Program:
import array as k
a=k.array('i')
n=int(input("Enter the size of the array:"))
print("Enter " , n , "elements in sorted order")
for i in range(0,n):
l=int(input())
a.append(l)
e=int(input("Enter the element to be searched:"))
pos=0
low=0
high=n-1
while(low<=high):
mid=int((low+high)/2)
if(e==a[mid]):
pos=mid+1
break
elif(e<a[mid]):
high=mid-1
else:
low=mid+1
if(pos==0):
print("Element not found")
else:
print("Element found")
print("Its position is at:",pos)

Time Complexity
Case Time Complexity

Best Case O(1)

Average Case O(logn)

Worst Case O(logn)

Recursive Binary Search


 We can also implement binary search using recursion.
def binarysearch(a,element,low,high):
if(high>=low):
mid=(low+high)//2
if(element==a[mid]):
return(mid+1)
elif(element<a[mid]):
return binarysearch(a,element,low,mid-1)
else:
return binarysearch(a,element,mid+1,high)
else:
return 0

import array as k
a=k.array('i')
n=int(input("Enter the size of the array:"))
print("Enter " , n , "elements in sorted order")
a=[0]*n
for i in range(0,n):
a[i]=int(input())
e=int(input("Enter the element to be searched:"))
pos=0
low=0
high=n-1
pos=binarysearch(a,e,low,high)
if(pos==0):
print("Element not found")
else:
print("Element found")
print("Its position is at:",pos)

Selection Sort

 Selection Sort algorithm is used to arrange a list of elements in a particular order


(Ascending or Descending).
 In selection sort, the first element in the list is selected and it is compared repeatedly
with all the remaining elements in the list.
 If any element is smaller than the selected element (for Ascending order), then both
are swapped so that first position is filled with the smallest element in the sorted
order.
 Next, we select the element at a second position in the list and it is compared with all
the remaining elements in the list. If any element is smaller than the selected element,
then both are swapped. This procedure is repeated until the entire list is sorted.

Step by Step Process

The selection sort algorithm is performed using the following steps...

Step 1 - Select the first element of the list (i.e., Element at first position in the list).

Step 2: Compare the selected element with all the other elements in the list.

Step 3: In every comparision, if any element is found smaller than the selected
element (for Ascending order), then both are swapped.

Step 4: Repeat the same procedure with element in the next position in the list till
the entire list is sorted.
Complexity of the Selection Sort Algorithm

To sort an unsorted list with 'n' number of elements, we need to make ((n-1)+(n-2)+(n-

3)+......+1) = (n (n-1))/2 number of comparisions in the worst case. If the list is already sorted

then it requires 'n' number of comparisions.

Worst Case : O(n2)

Best Case : Ω(n2)

Average Case : Θ(n2)

Program

import array as k
a=k.array(‘i’)
n=int(input("Number of elements in array:"))
print(" Enter ", n , " elements ")
for i in range(0,n):
l=int(input())
a.append(l)
print("Before sorting, array is:")
print(a)
for i in range(0,n):
for j in range(i+1,n):
if(a[i]>a[j]):
temp=a[i]
a[i]=a[j]
a[j]=temp
print("After sorting, array is:")
print(a)

Merge Sort
Merge sort algorithm uses Divide & Conquer technique.

In Divide & Conquer algorithm design paradigm, we divide the problems in sub-
problems recursively then solve the sub-problems, & at last combine the solutions to
find the final result.

One thing to keep in mind while dividing the problems into sub-problems is that, the
structure of sub-problems should not change as of the original problem.
Divide & Conquer algorithm has 3 steps:
1. Divide: Breaking the problem into subproblems
2. Conquer: Recursively solving the subproblems
3. Combine: Combining the solutions to get the final result
In Merge sort, we divide the array recursively in two halves, until each sub-array contains a
single element, and then we merge the sub-array in a way that it results into a sorted array.
merge() function merges two sorted sub-arrays into one, wherein it assumes that array[l .. n]
and arr[n+1 .. r] are sorted.

Merge sort is one of the efficient & fastest sorting algorithms with the following time
complexity:

Worst Case Time Complexity: O(n*log n)


Best Case Time Complexity: O(n*log n)
Average Time Complexity: O(n*log n)

Merge Sort Algorithm


MergeSort(arr, low, high), where low is the index of the first element & high is the index of the last
element.
If low<=highl

1. Find the middle index of the array to divide it in two halves: m


= (low+high)/2

2. Call MergeSort for first half:


mergeSort(array, low, mid)

3. Call mergeSort for second half:


mergeSort(array, mid+1,high)

4. Recursively, merge the two halves in a sorted manner, so that only one sorted array is left:
merge(array, low, mid,high)

Example:

1. Divide the unsorted array recursively until 1 element in each sub-array remains.

2. Recursively, merge sub-arrays to produce sorted sub-arrays until all the sub-array merges
and only one array remains.
Program
def mergesort(a,low,high):
if(low<high):
mid=int((low+high)/2)
mergesort(a,low,mid)
mergesort(a,mid+1,high)
merge(a,low,mid,high)

def merge(a,low,mid,high):
i=low
j=mid+1
k=low
while(i<=mid and j<=high):
if(a[i]<=a[j]):
b[k]=a[i]
k=k+1
i=i+1
else:
b[k]=a[j]
k=k+1
j=j+1
while(i<=mid):
b[k]=a[i]
k=k+1
i=i+1
while(j<=high):
b[k]=a[j]
k=k+1
j=j+1
i=low
while(i<k):
a[i]=b[i]
i+=1

import array as np
a=np.array('i')
b=np.array('i')
n=int(input("Number of elements in array:"))
a=[0]*n
b=[0]*n
print(" Enter ", n , " elements ")
for i in range(0,n):
a[i]=int(input())
print("Before sorting, array is:")
print(a)
low=0
high=n-1
mergesort(a,low,high)
print("After sorting, array is:")
print(a)

Time Complexity
 Merge Sort is a recursive algorithm and time complexity can be expressed as following
recurrence relation:
T(n) = 2T(n/2) + O(n)
 The solution of the above recurrence is O(nLogn).
 The list of size N is divided into a max of Logn parts, and the merging of all sublists into a
single list takes O(N) time, the worst-case run time of this algorithm is O(nLogn)
 Best Case Time Complexity: O(n*log n)
 Worst Case Time Complexity: O(n*log n)
 Average Time Complexity: O(n*log n)
 The time complexity of Merge Sort is O(n*Log n) in all the 3 cases (worst, average and best)
as the merge sort always divides the array into two halves and takes linear time to merge
two halves.

Bubble sort Algorithm

The working procedure of bubble sort is simplest.


Bubble sort works on the repeatedly swapping of adjacent elements until they are not in
the intended order.
It is called bubble sort because the movement of array elements is just like the movement
of air bubbles in the water. Bubbles in water rise up to the surface; similarly, the array
elements in bubble sort move to the end in each iteration.
The performance of bubble sort is poor in the real world. It is not suitable for large data
sets. The average and worst-case complexity of Bubble sort is O(n2), where n is a number of
items.

Bubble short is majorly used where -


o complexity does not matter
o simple and short code is
preferable

Preferred Algorithm

In the algorithm given below, suppose arr is an array of n elements. The assumed swap
function in the algorithm will swap the values of given array elements.

1. begin BubbleSort(arr)
2. for all array elements
3. if arr[i] > arr[i+1]
4. swap(arr[i], arr[i+1])
5. end if
6. end for
7. return arr
8. end BubbleSort

Working of Bubble sort Algorithm

Now, let's see the working of Bubble sort Algorithm.

To understand the working of bubble sort algorithm, let's take an unsorted


array. Let the elements of array are -

First Pass

Sorting will start from the initial two elements. Let compare them to check which is greater.

Here, 32 is greater than 13 (32 > 13), so it is already sorted. Now, compare 32 with 26.

Here, 26 is smaller than 36. So, swapping is required. After swapping new array will look like -

Now, compare 32 and 35.


Here, 35 is greater than 32. So, there is no swapping required as they are already
sorted.
Now, the comparison will be in between 35 and 10.Here, 10 is smaller than 35 that are
not sorted. So, swapping is required. Now, we reach at the end of the array. After first
pass, the array will be -

Now, move to the second iteration.

Second Pass

The same process will be followed for second iteration.

Here, 10 is smaller than 32. So, swapping is required. After swapping, the array will be -

Now, move to the third iteration.

Third Pass

The same process will be followed for third iteration.

Here, 10 is smaller than 26. So, swapping is required. After swapping, the array will be -

Now, move to the fourth iteration.

Fourth pass

Similarly, after the fourth iteration, the array will be -


Hence, there is no swapping required, so the array is completely sorted.

Implementing Bubble sort


import array as k
a=k.array(‘i’)
n=int(input("Number of elements in array:"))
print(" Enter ", n , " elements ")
for i in range(0,n):
l=int(input())
a.append(l)
print("Before sorting, array is:")
print(a)
for i in range(0,n):
for j in range(0,n-i-1):
if(a[j]>a[j+1]):
temp=a[j]
a[j]=a[j+1]
a[j+1]=temp
print("After sorting, array is:")
print(a)

Bubble sort complexity

1. Time Complexity

Case Time Complexity

Best Case O(n)

Average Case O(n2)

Worst Case O(n2)

o Best Case Complexity - It occurs when there is no sorting required, i.e. the array is
already sorted. The best-case time complexity of bubble sort is O(n).

o Average Case Complexity - It occurs when the array elements are in jumbled order that

is not properly ascending and not properly descending. The average case time
complexity of bubble sort is O(n2).

o Worst Case Complexity - It occurs when the array elements are required to be
sorted in reverse order. That means suppose you have to sort the array elements in
ascending

order, but its elements are in descending order. The worst-case time complexity of
bubble sort is O(n2).
Quick Sort
 Quick sort is the widely used sorting algorithm that makes n log n comparisons in average
case for sorting an array of n elements.
 It is a faster and highly efficient sorting algorithm.
 This algorithm follows the divide and conquer approach. Divide and conquer is a technique
of breaking down the algorithms into subproblems, then solving the subproblems, and
combining the results back together to solve the original problem.
Divide: In Divide, first pick a pivot element. After that, partition or rearrange the array into two sub-
arrays such that each element in the left sub-array is less than or equal to the pivot element and
each element in the right sub-array is larger than the pivot element.
Conquer: Recursively, sort two subarrays with Quicksort.
Combine: Combine the already sorted array.
 Quicksort picks an element as pivot, and then it partitions the given array around the picked
pivot element. In quick sort, a large array is divided into two arrays in which one holds
values that are smaller than the specified value (Pivot), and another array holds the values
that are greater than the pivot.
 After that, left and right sub-arrays are also partitioned using the same approach. It will
continue until the single element remains in the sub-array.

Choosing the pivot


Picking a good pivot is necessary for the fast implementation of quicksort. However, it is typical to
determine a good pivot. Some of the ways of choosing a pivot are as follows -
o Pivot can be random, i.e. select the random pivot from the given array.
o Pivot can either be the rightmost element of the leftmost element of the given array.
o Select median as the pivot element.

Working of Quick Sort Algorithm


Let the elements of array are -

In the given array, we consider the leftmost element as pivot. So, in this case, a[left] = 24, a[right] =
27 and a[pivot] = 24.
Since, pivot is at left, so algorithm starts from right and move towards left.

Now, a[pivot] < a[right], so algorithm moves forward one position towards left, i.e. -
Now, a[left] = 24, a[right] = 19, and a[pivot] = 24.
Because, a[pivot] > a[right], so, algorithm will swap a[pivot] with a[right], and pivot moves to right,
as -

Now, a[left] = 19, a[right] = 24, and a[pivot] = 24. Since, pivot is at right, so algorithm starts from
left and moves to right.
As a[pivot] > a[left], so algorithm moves one position to right as -

Now, a[left] = 9, a[right] = 24, and a[pivot] = 24. As a[pivot] > a[left], so algorithm moves one
position to right as -

Now, a[left] = 29, a[right] = 24, and a[pivot] = 24. As a[pivot] < a[left], so, swap a[pivot] and a[left],
now pivot is at left, i.e. -
Since, pivot is at left, so algorithm starts from right, and move to left. Now, a[left] = 24, a[right] =
29, and a[pivot] = 24. As a[pivot] < a[right], so algorithm moves one position to left, as -

Now, a[pivot] = 24, a[left] = 24, and a[right] = 14. As a[pivot] > a[right], so, swap a[pivot] and
a[right], now pivot is at right, i.e. -

Now, a[pivot] = 24, a[left] = 14, and a[right] = 24. Pivot is at right, so the algorithm starts from left
and move to right.

Now, a[pivot] = 24, a[left] = 24, and a[right] = 24. So, pivot, left and right are pointing the same
element. It represents the termination of procedure.
Element 24, which is the pivot element is placed at its exact position.
Elements that are right side of element 24 are greater than it, and the elements that are left side of
element 24 are smaller than it.
Now, in a similar manner, quick sort algorithm is separately applied to the left and right sub-arrays.
After sorting gets done, the array will be -

Program
def quicksort(a,low,high):
if (low<high):
pivot=partition(a,low,high)
quicksort(a,low,pivot-1)
quicksort(a,pivot+1,high)

def partition(a,low,high):
pivot=a[low]
i=high+1
for j in range(high,low,-1):
if (a[j] > pivot):
i=i-1
t = a[i]
a[i] = a[j]
a[j] = t
t=a[i-1]
a[i-1]=a[low]
a[low]=t
return(i-1)

import array as arr


a=arr.array('i')
n=int(input("Enter no. of elements in an array:"))
a=[0]*n
print("Enter elements of the array")
for i in range(0,n):
a[i]=int(input())
print("Before sorting array elements are - \n")
print(a)
quicksort(a,0,n-1)
print("\nAfter sorting array elements are - \n")
print(a)

Quicksort complexity
Time Complexity
Case Time Complexity

Best Case O(n*logn)

Average Case O(n*logn)

Worst Case O(n2)


o Best Case Complexity - In Quick sort, the best-case occurs when the pivot element is the
middle element or near to the middle element. The best-case time complexity of quicksort
is O(n*logn).
o Average Case Complexity - It occurs when the array elements are in jumbled order that is
not properly ascending and not properly descending. The average case time complexity of
quicksort is O(n*logn).
o Worst Case Complexity - In quick sort, worst case occurs when the pivot element is either
greatest or smallest element. Suppose, if the pivot element is always the last element of the
array, the worst case would occur when the given array is sorted already in ascending or
descending order. The worst-case time complexity of quick sort is O(n2).

Stacks:
 A stack is a linear data structure in which an element may be inserted or deleted at only one
end called top of the stack.
 Example: a stack of dishes, a stack of coins
 Stacks are also called a last-in-first-out (LIFO) list , that means the elements are removed
from a stack in the reverse order of that which they were inserted into the stack.
 They are also called piles or push-down lists.
 Special terminology used for two basic operations associated with stack are:
o Push is the term used to insert an element into the stack
o Pop is term used to delete an element from the stack
 The terms are used only with stacks and not with other data structures.

Operations:
Create():
 allocate memory block for the stack(array)
 implemented by using constructors
isempty():
 if top=-1,return 1 else return 0
 complexity is Θ(1)
isfull():
 if top=maxsize-1 return 1 else return 0
 complexity is Θ(1)
top()/ peek():
 return top most element in the list i.e., the element that is pointed by the top
 complexity is Θ(1)
push():
 if stack is full return a message stack overflow else increment top by 1 and push the
element into the list.
 Complexity is Θ(1)
pop():
 if stack is empty return a message stack underflow else remove the element pointed by top
and decrement top by 1.
 Complexity is Θ(1)
Overflow:
When the stack is full of elements and we try to push a new element into the stack then stack
overflow occurs.
Underflow:
If we try to remove an element from a stack, which contains no elements in it then it is called
stack underflow.

Implementing Stack using arrays in Python


import array as arr
class stack:
def __init__(self):
self.MAXSIZE=5
self.stack=arr.array('i')
self.stack=[0]*self.MAXSIZE
self.top=-1

def isempty(self):
if self.top==-1:
print("\n Stack is empty")
def isfull(self):
if self.top==self.MAXSIZE-1:
print("\n Stack is full")
def length(self):
if self.top==-1:
print("\n Stack is empty")
else:
print(" \n Stack contains ",self.top+1,"elements")

def push(self):
if self.top==self.MAXSIZE-1:
print("\n Stack overflow")
else:
value=int(input("\n Enter the value to be pushed into the stack"))
self.top+=1
self.stack[self.top]=value
def pop(self):
if self.top==-1:
print("\n Stack underflow")
else:
print("\n Value popped from the stack is=",self.stack[self.top])
self.top-=1
def display(self):
if self.top==-1:
print("\n Stack is empty")
else:
print("\n Stack contains=")
for i in range(self.top,-1,-1):
print(self.stack[i]," ",end='')

s=stack()
s.isempty()
s.push()
s.push()
s.push()
s.push()
s.push()
s.push()
s.display()
s.length()
s.pop()
s.display()

Characteristics of a Stacks:
 LIFO ordering: The last item pushed onto the stack is the first item popped off the stack.
 Push and Pop Operations: The two primary operations that can be performed on a stack
are "push," which adds an item to the top of the stack, and "pop," which removes the item
from the top of the stack.
 Top Element Access: A stack allows access only to the top element, which is the most
recently added item. Other elements are not directly accessible and must be removed first.
 Limited Accessibility: Stacks do not allow access to elements in the middle of the stack. To
access a specific element, all elements on top of it must be removed first.
 Dynamic Size: Stacks can grow or shrink dynamically as items are added or removed.
 Contiguous Memory Allocation: Stacks typically use contiguous memory allocation,
meaning that elements are stored in adjacent memory locations.
 Stack Overflow: A stack has a finite amount of memory allocated to it. When the stack
exceeds this memory limit, a stack overflow occurs.
 Stack Underflow: A stack underflow occurs when an attempt is made to remove an element
from an empty stack.
These characteristics make stacks useful for many applications, such as implementing
recursive function calls, undo-redo operations, and parsing expressions.
Applications of Stack
There are many applications of a stack. Some of them are:

 Stacks are used in implementing recursion.


 Stacks are used in equivalence class problem.
 Stacks are used in conversion of infix expression to postfix, evaluation of postfix expression
etc
 Stacks are used in backtracking algorithms.
 They are also used to implement undo/redo functionality in a software.
 Stacks are also used in syntax parsing for many compilers.
 Stacks are also used to check proper opening and closing of parenthesis i.e., Parenthesis
matching

Queues:
 A Queue is a linear data structure in which additions and deletions take place at different
ends.
 The end at which new elements are added is called the rear
 The end at which the old elements are deleted is called the front.
 The terms front and rear are used in describing a linear list only when it is implemented as
a queue.
 Queues are also called first-in-first-out(FIFO) lists .
 Thus in queue the order in which the elements enter into a queue is the order in which
they leave.
 Ex: people waiting in line at a bank.

Operations
Create():
 allocate memory block for the queue(array)
 implemented by using constructors
isempty():
 if front=-1,return 1 else return 0
 complexity is Θ(1)
isfull():
 if rear=maxsize-1 return 1 else return 0
 complexity is Θ(1)
insert():
 if queue is full return a message queue overflow else increment rear by 1 and insert the
element into the queue.
 Complexity is Θ(1)
delete():
 if queue is empty return a message queue underflow else remove the element pointed by
front and increment front by 1.
 Complexity is Θ(1)
Overflow:
When the queue is full of elements and we try to insert a new element into the queue then
queue overflow occurs.
Underflow:
If we try to remove an element from a queue, which contains no elements in it then it is called
queue underflow.

Implement queue using arrays

import array as arr


class queue:
def __init__(self):
self.MAXSIZE=5
self.queue=arr.array('i')
self.queue=[0]*self.MAXSIZE
self.front=-1
self.rear=-1

def isempty(self):
if self.front==-1:
print("\n Queue is empty")
def isfull(self):
if self.rear==self.MAXSIZE-1:
print("\n Queue is full")
def length(self):
if self.front==-1:
print("\n Queue is empty")
else:
print(" \n Queue contains ",self.rear-self.front+1,"elements")

def insert(self):
if(self.rear==self.MAXSIZE-1):
print("Queue Overflow")
else:
value=int(input("\n Enter the value to be pushed into the queue"))
if self.front==-1:
self.front+=1
self.rear+=1
else:
self.rear+=1
self.queue[self.rear]=value
def delete(self):
if self.front==-1:
print("\n Queue underflow")
else:
print("\n Value deleted from the queue is=",self.queue[self.front])
if self.front==self.rear:
self.front=self.rear=-1
else:
self.front+=1

def display(self):
if self.front==-1:
print("\n Queue is empty")
else:
print("\n Queue contains=")
for i in range(self.front,self.rear+1):
print(self.queue[i]," ",end='')

s=queue()
s.isempty()
s.insert()
s.insert()
s.insert()
s.insert()
s.insert()
s.insert()
s.display()
s.length()
s.delete()
s.display()
s.delete()
s.display()
Applications of Queue:.
 Railroad car Rearrangement
 Wire Routing
 Image-Component Labeling
 Machine Shop Simulation

Difference between Stack and Queue Data Structures


Stack Queue
1. It follows the LIFO (Last In First Out) order 1. It follows the FIFO (First In First Out) order to store the
to store the elements, which means the elements, which means the element that is inserted first will
element that is inserted last will come out come out first.
first.
2. It has only one end, known as the top, at 2. It has two ends, known as the rear and front, which are used
which both insertion and deletion take place. for insertion and deletion. The rear end is used to insert the
elements, whereas the front end is used to delete the elements
from the queue.
3. The insertion operation is known 3. The insertion operation is known as enqueue and the
as push and the deletion operation is known deletion operation is known as dequeue.
as pop.
4. The condition for checking whether the 4. The condition for checking whether the queue is empty
stack is empty is top ==-1 as -1 refers to no is front == -1
element in the stack.
5. The condition for checking if the stack is 5. The condition for checking if the queue is full is rear==max-
full is top==max-1 as max refers to the 1 as max refers to the maximum number of elements that can
maximum number of elements that can be in be in the queue.
the stack.
6. There are no other variants or types of the 6. There are three types of queues known as circular, double-
stack. ended, and priority.
7. It has a simple implementation compared 7. It has a complex implementation compared to stacks as two
to queues as no two pointers are involved. pointers front and rear are involved.
8. It is used to solve recursion-based 8. It is used to solve sequential processing-based problems.
problems.
9. A real-life example of a stack can be the 9. A real-life example of a queue can be an operating system
Undo/Redo operation in Word or Excel. process scheduling queues

Circular Queue
 Array implementation of queue suffers from one limitation.
 In the implementation when the rear pointer reaches at the end, insertion will be denied
even if sufficient space available at front.
 To overcome the above limitation we can implement the queue as a circular queue.
A circular queue is a queue in which all locations are treated as circular such that the first
location q[0] follows the last location q[max-1]

Implement circular queue in python


import array as arr
class cqueue:
def __init__(self):
self.MAXSIZE=5
self.cq=arr.array('i')
self.cq=[0]*self.MAXSIZE
self.front=-1
self.rear=-1

def isempty(self):
if self.front==-1:
print("\n Queue is empty")
else:
print("\n Queue is not empty")
def isfull(self):
if ((self.rear+1)%self.MAXSIZE==self.front):
print("\n Queue is full")
else:
print("\n Queue is not full")
def length(self):
if self.front==-1:
print("\n Queue is empty")
else:
if(self.front<=self.rear):
print(" \n Queue contains ",self.rear-self.front+1,"elements")
else:
print(" \n Queue contains ",self.MAXSIZE-self.front+self.rear+1,"elements")

def insert(self):
if ((self.rear+1)%self.MAXSIZE==self.front):
print("Queue Overflow")
else:
value=int(input("\n Enter the value to be pushed into the queue"))
if self.front==-1:
self.front+=1
self.rear+=1
else:
self.rear=(self.rear+1)%self.MAXSIZE
self.cq[self.rear]=value
def delete(self):
if self.front==-1:
print("\n Queue underflow")
else:
print("\n Value deleted from the circular queue is=",self.cq[self.front])
if self.front==self.rear:
self.front=self.rear=-1
else:
self.front=(self.front+1)%self.MAXSIZE

def display(self):
if self.front==-1:
print("\n Queue is empty")
else:
print("\n Queue contains=")
if(self.front<=self.rear):
for i in range(self.front,self.rear+1):
print(self.cq[i]," ",end='')
else:
for i in range(self.front,self.MAXSIZE):
print(self.cq[i]," ",end='')
for i in range(0,self.rear+1):
print(self.cq[i]," ",end='')

s=cqueue()
s.isempty()
s.insert()
s.insert()
s.insert()
s.insert()
s.insert()
s.insert()
s.display()
s.length()
s.delete()
s.display()
s.delete()
s.display()
s.insert()
s.display()
s.length()

Single Linked List


 In a linked list, each element of an instance of a data object is represented in a cell or node.
 Each node keeps explicit information about the location of other relevant nodes.
 This explicit information about the location of another node is called a link or reference.
 Each node has exactly one link field that is used to locate the next element in the list.
 The last node has no node to link to and so its link field is None.
 The variable start points to the first node in the representation.
 Since each node in the linked representation has exactly one link, the structure is called
singly linked list.
 The structure is also called chain.

Operations
Create():
 create an empty list
 implemented by using constructor.
isempty():
 return true if the list is empty, false otherwise
 the complexity of isempty() is Θ(1)
length():
 return the list size i.e., number of elements in the list
 complexity of length() is Θ(n)
find(k,x):
 return the kth element of the list in x, return false if there is no kth element.
 Complexity of find() is O(K)
Search(x):
 return the position of x in the list
 return 0 if x is not present in the list
 complexity of search() is O(n)
Delete(k,x):
 delete kth element and return it in x.
 this function returns the modified list
 complexity of Delete() is O(k)
Insert(k,x):
 insert x just after the k th element.
 This function returns the modified list
 complexity of Delete() is O(k)
Output():
 output the list from left to right
 Complexity of output() is Θ(length)

Implementing Single Linked list in Python


class node:
def __init__(self,data):
self.data=data
self.link=None
class linkedlist:
def __init__(self):
self.start=None
def isempty(self):
if self.start==None:
print("\n Linked list is empty")
else:
print("\n Linked list is not empty")
def length(self):
if self.start==None:
print("\n Linked list is empty")
else:
count=0
temp=self.start
while(temp!=None):
temp=temp.link
count+=1
print(" Linked List contains :",count , "nodes")
def insert(self):
print("\n Enter the value which you want to insert ")
newdata=int(input())
newnode=node(newdata)
if self.start==None:
self.start=newnode
else:
newnode.link=self.start
self.start=newnode
def display(self):
if self.start==None:
print("\n Linked list is empty")
else:
print(" \n Linked List is:")
temp=self.start
while(temp!=None):
print(temp.data,"->",end='')
temp=temp.link
def delete(self):
if self.start==None:
print("Underflow")
else:
temp=self.start
self.start=self.start.link
print("Deleted node is=",temp.data)

l=linkedlist()
l.display()
l.insert()
l.insert()
l.insert()
l.display()
l.isempty()
l.length()
l.delete()
l.display()

You might also like