0% found this document useful (0 votes)
2 views59 pages

Unit 2 Notes

The document provides an overview of linear structures, specifically focusing on List Abstract Data Types (ADTs) with array-based and linked list implementations. It explains the characteristics and operations of arrays and linked lists, including methods for adding, accessing, removing, and updating elements. Additionally, it covers singly linked lists and circularly linked lists, detailing their creation, insertion, deletion, and time complexities.

Uploaded by

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

Unit 2 Notes

The document provides an overview of linear structures, specifically focusing on List Abstract Data Types (ADTs) with array-based and linked list implementations. It explains the characteristics and operations of arrays and linked lists, including methods for adding, accessing, removing, and updating elements. Additionally, it covers singly linked lists and circularly linked lists, detailing their creation, insertion, deletion, and time complexities.

Uploaded by

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

UNIT II LINEAR STRUCTURES

LIST ADT – ARRAY-BASED IMPLEMENTATIONS

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.
List ADT -Array Abstract Data Type

• An array is a collection of items stored at contiguous memory locations. The


idea is to store multiple items of the same type together.
Example:
we can think of an array a fleet of stairs where on each step is placed a value
(let’s say one of your friends).
Here, you can identify the location of any of your friends by simply knowing
the count of the step they are on.
HANDLING:
• Array can be handled in Python by a module named array. They can be useful
when we have to manipulate only a specific data type values.
• If you create arrays using the array module, all elements of the array must be
of the same type.
Adding Elements to a Array

Elements can be added to the Array by using built-in insert() function. Insert is used
to insert one or more data elements into an array. Based on the requirement, a new
element can be added at the beginning, end, or any given index of array. append() is
also used to add the value mentioned in its arguments at the end of the array.
SYNTAX

Array_name.insert(1, 4)
Array_name.append(4)
Accessing elements from the Array

• In order to access the array items refer to the index number.


• Use the index operator [ ] to access an item in a array.
• The index must be an integer.
To print value in specific index:
print( array_name[index_value])
Removing Elements from the Array
• Elements can be removed from the array by using built-in remove() function
but an Error arises if element doesn’t exist in the set. Remove() method only
removes one element at a time, to remove range of elements, iterator is used.
• pop() function can also be used to remove and return an element from the
array, but by default it removes only the last element of the array, to remove
element from a specific position of the array, index of the element is passed as
an argument to the pop() method.
Syntax:

Array_name.remove()
Array_name.pop()
Slicing of a Array

• print a specific range of elements from the array


• Slice operation is performed on array with the use of colon(:)
• To print elements from beginning to a range use [:Index],
• to print elements from end use [:-Index],
• to print elements from specific Index till the end use [Index:],
• to print elements within a range, use
• [Start Index:End Index]
• to print whole List with the use of slicing operation, use [:].
• Further, to print whole array in reverse order, use [::-1].
Searching element in a Array

• In order to search an element in the array we use a python in-


built index() method.
• This function returns the index of the first occurrence of value mentioned in
arguments.
Syntax:

print (Array_name.index(value))

Updating Elements in a Array

In order to update an element in the array we simply reassign a new value to the
desired index we want to update.
Syntax:

Array_name[index] = new value

Example Program:

import array as arr;


list1=[];
n = int(input("Enter number of elements : "));
print("Enter the elements:");
for i in range(0, n):
ele = int(input());
list1.append(ele);
a = arr.array('i', list1);
print("\nArray: ")
for i in range(0,len(a)):
print(a[i],end=" ");
e=int(input("\nEnter the index:"));
print("Access element is: ", a[e]);
b=int(input("\nEnter the element which need to find index:"));
print ("\nIndex of the element",b,"is",a.index(b));
print("\npoping:");
p=int(input("Enter the index of the value that need to be remove:"));
a.pop(p);
print("Array: ")
for i in range(0,len(a)):
print("index",i,"value",a[i]);
print("\nRemove method:");
r=int(input("Enter the element which need be remove:"));
a.remove(r);
print("Array:")
for i in range(0,len(a)):
print("index",i,"value",a[i]);
print("\nSearching:")
s=int(input("Enter the value to find the index:"));
print(a.index(s));
print("\nUpdating:")
u=int(input("Enter the index:"));
v=int(input("Enter the value:"));
a[u]=v;
print("Array: ")
for i in range(0,len(a)):
print(a[i],end=" ");
print("\nSlicing");
f=int(input("Enter the start index:"));
l=int(input("Enter the end index:"));
print(a[f:l]);
Output:
LIST ADT- LINKED LIST IMPLEMENTATIONS

Linked List Implementation


Linked list provides an alternative to an arraybased sequence a linked list in contrast, relies
on a more distributed representation in which a light weight object known as a node, is
allocated for each element. Each nodes maintains a reference to its element and one or more
reference to neighbouring in order to collectively represent the linear order of the sequence.

List ADT
A linked list in programming terms is an Abstract Data Type that acts as a linear collection of
data elements organized as a collection of nodes that contains information about what that
node contains and then a link to another node.
The benefit of this over a regular array or list is that elements can be easily inserted and
removed without the need of changing the index of all other items and the memory used to
store the linked list.
This data structure can be useful when:
• You want to insert items easily in between other items
• The size of the total collection is unknown
• You don’t need random access when searching for item
• There is no concern about memory usage for storing the data
Key methods commonly used in list ADT:
• insert(): Add an item to the linked list at the head of the list
• find(): Find an item within the linked list
• remove(): Remove a given item with a given value
• is_empty(): Returns whether the linked list is empty or not
• get_count(): Returns the number of items in the linked list
INSERT()
*Create a new node at the Head of the Linked List
*This has a time complexity of O(1) as we are simply changing the current head of
the Linked List and no indices have to change.
FIND()
*Search for item in Linked List with data = val
*Time complexity is O(n) as in worst case scenario have to iterate over whole Linked
List
* The item we can then return that certain Node, or in the case that that value does not
exist then we can return None to avoid any errors being thrown.

REMOVE()
*Remove Node with value equal to item
*Time complexity is O(n) as in the worst case we have to iterate over the whole
linked list.

GET_COUNT
*Return the length of the Linked List
*Time complexity O(1) as only returning a single value

IS_EMPTY
*Returns whether the Linked List is empty or not
*Time complexity O(1) as only returns True or False

Program:
class Node:
def __init__(self,val):
self.val=val
self.next=None
class LinkedList():
def __init__(self,head=None):
self.head=head
self.count=0
def insert(self,data):
new_node=Node(data)
mew_node.next=self.head
self.head=new_node
self.count+=1
def find(self,val):
item=self.head
while item!=None:
if item.val==val:
return item
else:
item=item.next
return None
def remove(self,item):
current=self.head.next
previous=self.head
while current is not None:
if current.val==item:
break

previous=previous.next
current=current.next
if previous is None:
self.head=current.next
self.count-=1
def get_count(self):
return self.count
def is_empty(self):
return self.head==None
def display(self):
temp=self.head
while temp:
print9temp.val,"-->",end="")
temp=temp.next
print()

obj=LinkedList()
obj.insert(10)
obj.insert(20)
obj.insert(30)
obj.insert(40)
obj.insert(50)
obj.display()
print(obj.find(30))
print(obj.remove(40))
obj.display()
print(obj.get_count())
obj.is_empty()
Output:

SINGLY LINKED LIST

 The singly linked list is a linear data structure in which each element of the list
contains a pointer which points to the next element in the list.
 Each element in the singly linked list is called a node.
 Each node has two components: data and a pointer next which points to the next node
in the list.
 The first node of the list is called as head, and the last node of the list is called a tail.
 The last node of the list contains a pointer to the null.
 Each node in the list can be accessed linearly by traversing through the list from head
to tail.
Example:

The operations involved in Singly Linked List are


 Creation
 Display
 Insertion
o Insertion at Front
o Insertion at End
o Insertion at Middle
 Deletion
o Deletion at Front
o Deletion at End
o Deletion at Middle

Creation and Display


1 Create a class Node which has two attributes: data and next. Next is a pointer to the
next node.
2 Create another class in which initially the head node is None.
3 In display function it checks whether the head node is empty or not. If the node is
empty then it will returns “Linked list is empty”.
4 Otherwise, it display the elements in the linked list.

Insertion
Inserting at the Beginning

Step 1:
Adding node at the front of the existing node.
nb=Node(data)
nb=self.head
self.head=nb
Output:

Time Complexity: O(1), We have a pointer to the head and we can directly attach a node
and change the pointer. So the Time complexity of inserting a node at the head position is
O(1) as it does a constant amount of work.

Inserting at the End:


Step 1:

Inserting the new node at the end of the liked list.


ne=Node(data)
temp=self.head
while temp.next
temp=temp.next
temp.next=ne
Output:
Time complexity: O(N), where N is the number of nodes in the linked list. Since there is a
loop from head to end, the function does O(n) work.

Inserting at the Middle:


Step 1:

np=Node(data)
temp=self.head
for i in range(pos-1):
temp=temp.next
np.data=data
np.next=temp.next
temp.next=np
Output

Time complexity: O(1), since prev_node is already given as argument in a method, no


need to iterate over list to find prev_node
Deletion:
Deletion at Beginning

temp=self.head
self.head=self.head.next
temp.next=None
Output

Time Complexity: O(1)


Deletion at End:

temp=self.head.next
prev=self.head
while temp.next is not None:
temp=temp.next
prev=prev.next
prev.next=None
Output:

Time Complexity: O(N)


Deletion at Middle:

temp=self.head.next
prev=self.head
for i in range(1,pos-1):
temp=temp.next
prev=prev.next
prev.next=temp.next

Output:

Time Complexity: O(N)

Program:

class Node:

def __init__(self, data):

self.data = data

self.next = None

class SinglyLinkedList:

def __init__(self):

self.head = None
defInsert_Beg(self,data):

new = Node(data)

new.next = self.head

self.head = new

defInsert_Mid(self,pos,data):

new = Node(data)

temp = self.head

for i in range(pos-1):

temp = temp.next

new.next = temp.next

temp.next = new

defInsert_End(self,data):

temp = self.head

while temp.next:

temp = temp.next

temp.next = Node(data)

defDelete_Beg(self):

temp = self.head

self.head = self.head.next

temp.next = None

defDelete_Mid(self,pos):

temp = self.head.next

prev = self.head
for i in range(1,pos-1):

temp = temp.next

prev = prev.next

prev.next = temp.next

defDelete_End(self):

temp = self.head.next

prev = self.head

while temp.next is not None:

temp = temp.next

prev = prev.next

prev.next = None

def Display(self):

if self.head is None:

print("The Linked List is Empty")

else:

temp = self.head

while temp:

print(temp.data,"-->",end = "")

temp = temp.next

print()

obj = SinglyLinkedList()

n1 = Node(8)

obj.head = n1

n2 = Node(3)
n1.next = n2

n3 = Node(7)

n2.next = n3

n4 = Node(2)

n3.next = n4

n5 = Node(9)

n4.next = n5

obj.Display()

print("Singly Linked List Creation\n")

obj.Insert_Beg(20)

obj.Display()

print("Singly Linked List Insert Beginning\n")

obj.Insert_Mid(3,30)

obj.Display()

print("Singly Linked List Insert Middle at 3\n")

obj.Insert_End(40)

obj.Display()

print("Singly Linked List Insert End\n")

obj.Delete_Beg()

obj.Display()

print("Singly Linked List Delete Beginning\n")

obj.Delete_Mid(2)

obj.Display()

print("Singly Linked List Delete Middle at 2\n")

obj.Delete_End()

obj.Display()

print("Singly Linked List Delete End")


Output:

CIRCULARLY LINKED LISTS

 The circular linked list is a kind of linked list.


 The node is an element of the list, and it has two parts that are, data and next.
 Data represents the data stored in the node, and next is the pointer that will point to
the next node.
 Here the last node will point to the head node.

The operations involved in Circularly Linked List are


 Creation
 Display
 Insertion
o Insertion at Front
o Insertion at End
o Insertion at Middle
 Deletion
o Deletion at Front
o Deletion at End
o Deletion at Middle
CREATION

DISPLAY

Insertion

Insertion at Beginning
Insertion at End

Insertion at Middle
Deletion

Deletion at Beginning

Deletion at End
Deletion at Middle

Circular Linked List Complexity

Circular Linked List Complexity Time Complexity Space Complexity

Insertion Operation O(1) or O(n) O(1)

Deletion Operation O(1) O(1)

1. Complexity of Insertion Operation


 The insertion operations that do not require traversal have the time complexity of O(1).
 And, an insertion that requires traversal has a time complexity of O(n).
 The space complexity is O(1).

2. Complexity of Deletion Operation


 All deletion operations run with a time complexity of O(1).
 And, the space complexity is O(1).

Why Circular Linked List?


1. The NULL assignment is not required because a node always points to another node.
2. The starting point can be set to any node.
3. Traversal from the first node to the last node is quick.
Circular Linked List Applications
 It is used in multiplayer games to give a chance to each player to play the game.
 Multiple running applications can be placed in a circular linked list on an operating system.
The os keeps on iterating over these applications.
Program:

class Node:

def __init__(self, data):

self.data = data

self.next = None

class CircularlyLinkedList:

def __init__(self):

self.head = None

self.tail = None

self.nodes = 0

def Display(self):

temp = self.head

for i in range(self.nodes):

print(temp.data,"-->",end = " ")

temp = temp.next

print()

defInsert_Beg(self,data):

self.nodes += 1

new = Node(data)

temp = self.head

new.next = temp
self.head = new

self.tail.next = new

defInsert_Mid(self,pos,data):

self.nodes += 1

new = Node(data)

temp = self.head

for i in range(pos-1):

cur = temp

temp = temp.next

cur.next = new

new.next = temp

defInsert_End(self, data):

self.nodes += 1

new = Node(data)

if self.head is None:

self.head = new

self.tail = new

else:

temp = self.head

for i in range(self.nodes-2):

temp = temp.next

temp.next = new

self.tail = new

new.next = self.head

defDelete_Beg(self):

self.nodes -= 1

temp = self.head
self.head = self.head.next

temp.next = None

self.tail.next = self.head

defDelete_Mid(self,pos):

self.nodes -= 1

temp = self.head.next

prev = self.head

for i in range(1,pos-1):

temp = temp.next

prev = prev.next

prev.next = temp.next

defDelete_End(self):

self.nodes -= 1

temp = self.head.next

prev = self.head

for i in range(self.nodes-1):

temp = temp.next

prev = prev.next

prev.next = temp.next

temp.next = None

self.tail = prev

obj = CircularlyLinkedList()

n1 = Node(8)

obj.head = n1

n2 = Node(3)

n1.next = n2

n3 = Node(7)
n2.next = n3

n4 = Node(2)

n3.next = n4

n5 = Node(9)

n4.next = n5

obj.tail = n5

n5.next = n1

obj.nodes = 5

obj.Display()

print("Circular Linked List Creation\n")

obj.Insert_Beg(20)

obj.Display()

print("Circular Linked List Insertion at Beginning\n")

obj.Insert_Mid(3,30)

obj.Display()

print("Circular Linked List Insertion at Middle at Position 3\n")

obj.Insert_End(40)

obj.Display()

print("Circular Linked List Insertion at End\n")

obj.Delete_Beg()

obj.Display()

print("Circular Linked List Deletion at Beginning\n")

obj.Delete_Mid(2)

obj.Display()

print("Circular Linked List Deletion at Middle at Position 2\n")

obj.Delete_End()

obj.Display()
print("Circular Linked List Deletion at End")

Output:

DOUBLY LINKED LIST

A Doubly Linked List (DLL) contains an extra pointer, typically called the previous pointer,
together with the next pointer and data which are there in the singly linked list.

 Link − Each link of a linked list can store a data called an element.
 Next − Each link of a linked list contains a link to the next link called Next.
 Prev − Each link of a linked list contains a link to the previous link called Prev.
 LinkedList − A Linked List contains the connection link to the first link called First
and to the last link called Last.

The operations involved in Doubly Linked List are


 Creation
 Display
 Insertion
o Insertion at Front
o Insertion at End
o Insertion at Middle
 Deletion
o Deletion at Front
o Deletion at End
o Deletion at Middle

Creation
class Node:

def __init__(self, data):

self.data = data

self.prev = None

self.next = None

class DoublyLinkedList:

def __init__(self):

self.head = None

self.tail = None

Display
def Display(self):

temp = self.head

while temp:

print("<-->",temp.data,end = " ")

temp = temp.next

print("<-->")

Insertion
Insertion at Beginning
defInsert_Beg(self,data):

new = Node(data)

new.next = self.head

self.head.prev = new

new.prev = None

self.head = new

Insertion at End

defInsert_End(self,data):

new = Node(data)

if self.head is None:

new.prev = None

self.head = new

else:

temp = self.head

while temp.next:

temp = temp.next

temp.next = new

new.prev = temp

self.tail = new

Insertion at Middle
defInsert_Mid(self,pos,data):

new = Node(data)

temp = self.head

for i in range(pos-1):

temp = temp.next

new.next = temp.next

temp.next = new

new.prev = temp

new.next.prev = new

Deletion

Deletion at Beginning

defDelete_Beg(self):

temp = self.head.next
self.head.next = None

self.head = temp

temp.prev = None

Deletion at End

defDelete_End(self):

temp = self.head.next

cur = self.head

while temp.next:

temp = temp.next

cur = cur.next

temp.prev = None

cur.next = None

Deletion at Middle

defDelete_Mid(self,pos):

temp = self.head.next

cur = self.head
for i in range(1,pos):

temp = temp.next

cur = cur.next

cur.next = temp.next

temp.next.prev = cur

temp.prev = None

temp.next = None

Example Program:
class Node:

def __init__(self, data):

self.data = data

self.prev = None

self.next = None

class DoublyLinkedList:

def __init__(self):

self.head = None

self.tail = None

defInsert_Beg(self,data):

new = Node(data)

new.next = self.head

self.head.prev = new

new.prev = None

self.head = new

defInsert_Mid(self,pos,data):
new = Node(data)

temp = self.head

for i in range(pos-1):

temp = temp.next

new.next = temp.next

temp.next = new

new.prev = temp

new.next.prev = new

defInsert_End(self,data):

new = Node(data)

if self.head is None:

new.prev = None

self.head = new

else:

temp = self.head

while temp.next:

temp = temp.next

temp.next = new

new.prev = temp

self.tail = new

defDelete_Beg(self):

temp = self.head.next

self.head.next = None

self.head = temp

temp.prev = None
defDelete_Mid(self,pos):

temp = self.head.next

cur = self.head

for i in range(1,pos):

temp = temp.next

cur = cur.next

cur.next = temp.next

temp.next.prev = cur

temp.prev = None

temp.next = None

defDelete_End(self):

temp = self.head.next

cur = self.head

while temp.next:

temp = temp.next

cur = cur.next

temp.prev = None

cur.next = None

def Display(self):

temp = self.head

while temp:

print("<-->",temp.data,end = " ")

temp = temp.next

print("<-->")
obj = DoublyLinkedList()

n1 = Node(8)

obj.head = n1

n1.prev = None

n2 = Node(3)

n1.next = n2

n2.prev = n1

n3 = Node(7)

n2.next = n3

n3.prev = n2

n4 = Node(2)

n3.next = n4

n4.prev = n3

n5 = Node(9)

n4.next = n5

n5.prev = n4

obj.tail = n5

n5.next = None

obj.Display()

print("Doubly Linked List Creation\n")

obj.Insert_Beg(20)

obj.Display()

print("Doubly Linked List Insertion at Beginning\n")

obj.Insert_Mid(3,30)

obj.Display()

print("Doubly Linked List Insertion at Middle at Position 3\n")


obj.Insert_End(40)

obj.Display()

print("Doubly Linked List Insertion at End\n")

obj.Delete_Beg()

obj.Display()

print("Doubly Linked List Deletion at Beginning\n")

obj.Delete_Mid(2)

obj.Display()

print("Doubly Linked List Deletion at Middle at Position 2\n")

obj.Delete_End()

obj.Display()

print("Doubly Linked List Deletion at End")

Output:

STACK ADT

 A Stack is a linear data structure that follows the LIFO (Last-In-First-


Out) principle. Stack has one end.
 It contains only one pointer top pointer pointing to the topmost element of the stack.
 Whenever an element is added in the stack, it is added on the top of the stack, and the
element can be deleted only from the stack.
 In other words, a stack can be defined as a container in which insertion and deletion
can be done from the one end known as the top of the stack.
Some key points related to stack

o It is called as stack because it behaves like a real-world stack, piles of books, etc.
o A Stack is an abstract data type with a pre-defined capacity, which means that it can
store the elements of a limited size.
o It is a data structure that follows some order to insert and delete the elements, and that
order can be LIFO or FILO.

Working of Stack

Stack works on the LIFO pattern. As we can observe in the below figure there are five
memory blocks in the stack; therefore, the size of the stack is 5.
Suppose we want to store the elements in a stack and let's assume that stack is empty.
We have taken the stack of size 5 as shown below in which we are pushing the
elements one by one until the stack becomes full.

Standard Stack Operations

o push(): When we insert an element in a stack then the operation is known as a push.
If the stack is full then the overflow condition occurs.
o pop(): When we delete an element from the stack, the operation is known as a pop. If
the stack is empty means that no element exists in the stack, this state is known as an
underflow state.
o isEmpty(): It determines whether the stack is empty or not.
o isFull(): It determines whether the stack is full or not.'
o peek(): It returns the element at the given position.
o count(): It returns the total number of elements available in a stack.
o change(): It changes the element at the given position.
o display(): It prints all the elements available in the stack.

PUSH operation

o Before inserting an element in a stack, we check whether the stack is full.


o If we try to insert the element in a stack, and the stack is full, then
the overflow condition occurs.
o When we initialize a stack, we set the value of top as -1 to check that the stack is
empty.
o When the new element is pushed in a stack, first, the value of the top gets
incremented, i.e., top=top+1, and the element will be placed at the new position of
the top.
o The elements will be inserted until we reach the max size of the stack.

POP operation

o Before deleting the element from the stack, we check whether the stack is empty.
o If we try to delete the element from the empty stack, then the underflow condition
occurs.
o If the stack is not empty, we first access the element which is pointed by the top
o Once the pop operation is performed, the top is decremented by 1, i.e., top=top-1.
APPLICATIONS OF STACK
• Matching tags in HTML and XML
• Stack is used for evaluating expression with operands and operations.
• Undo function in any text editor.
• Infix to Postfix conversion.
• Stacks are used for backtracking and parenthesis matching.
• Stacks are used for conversion of one arithmetic notation to another arithmetic
notation.
• Stacks are useful for function calls, storing the activation records and deleting them
after returning from the function. It is very useful in processing the function calls.
• Stacks help in reversing any set of data or strings.
ALGORITHM:
■ STEP 1: Start the program

■ STEP 2:Create a stack using class

■ STEP 3:defining a class variable that is calling items and it is assigned to an empty
list
■ STEP 4:create a push method push(self.item).here, item is an argument

■ STEP 5: using append() method to add elements into the list.

■ STEP 6:Create a pop() method and get_stack() method, this will returns the items list

■ STEP 7:Create an object for class stack()


■ STEP 8:Operating push method and print first two element and then we added another
one element using push()method
■ STEP 9: After step 8 operating pop method for the list and the last entered element
was first poped.
■ STEP 10:Stop the program

Program:

OUTPUT:

Implement Stack using List


OUTPUT
APPLICATION OF STACK
Infix Notation
When the operator is placed between its operands, it is known as infix notation. The operand
is not necessarily a constant or a variable—it can also be an expression.

For example: (a + b) * (c + d)

The above expression contains three operators. The operands for the first plus operator are a
and b, whereas the operands for the second plus operator are c and d. Evaluating the result of
these operations requires applying some set of rules. At last, after using the addition operator
on (a + b) and (c + d), the multiplication operation will be performed to look towards the final
answer.

Syntax:

<operand> <operator> <operand>

Postfix Notation
The expression in which the operator is written after the operands is known as postfix
expression or reverse polish notation.

For example, the postfix notation of infix expression (a + b) can be written as ab+.
Postfix expression is an arithmetic expression in which operators are applied from left to
right. There is no need for parentheses while working with postfix notation, unlike infix
expressions. Moreover, no operator precedence rules or associativity rules are needed,
meaning that coders do not need to memorize a special set of rules to help them determine the
order in which operations will be performed.

Algorithm for postfix notation


Let the infix notation of an expression be: a + b * c

Here, we will start scanning the expression from the left side. As the multiplication operator
is the first operator appearing while scanning expression from left to right, the expression will
be

Resultant Expression 1: a + bc*

Again, we scan the expression from left to right, and the next operator encountered will be
plus. As a result, the final output of the expression will be

Resultant Expression 2: abc+*

Therefore, the final algorithmic steps for the postfix expression are:

1) Scanning the expression from left to right


2) If the operand is encountered, push it into the stack
3) If the operator is encountered, then pop the corresponding operand from the stack and
perform computation
4) Continue the same process and retain the final value in the stack

Rules for converting Infix to postfix expression

Using the stack data structure is the best method for converting an infix expression to a
postfix expression. It holds operators until both operands have been processed, and it reverses
the order of operators in the postfix expression to match the order of operation.

Here is an algorithm that returns the string in postfix order, left to right. For each token, there
are four cases:

1. If the current token is an opening parenthesis, push it into the stack.


2. If the current token is a closing parenthesis, pop tokens from the stack until a
corresponding opening parenthesis are popped. Append each operator to the end of
the postfix expression.
3. Append the current token to the end of the postfix expression if it is an operand
4. If the current token is an operator, append it to the postfix expression with higher
precedence than the operator on top of the stack. If it has lower precedence, first pop
operators from the stack until we have a higher precedence operator on top, or the
stack becomes empty.
5. Finally, if you have any remaining operators in the stack, add them to the end of the
postfix expression until the stack is empty and return the postfixed expression.

Example
Let the expression to be evaluated be m*n+(p-q)+r

Steps Current Token Operation on Stack Stack Representation Postfix Expression

1 M m

2 * Push * m

3 N * mn

4 + Push + mn*

5 ( Push +( mn*

6 P +( mn*p

7 - Push +(- mn*p

8 Q +(- mn*pq

Pop and append until the open


9 ) + mn*pq-
bracket

10 + Push + mn*pq-+

11 R + mn*pq-+r

12 End Pop until the stack is empty mn*pq-+r+

Program:
Operators = set(['+', '-', '*', '/', '(', ')', '^']) # collection of Operators

Priority = {'+':1, '-':1, '*':2, '/':2, '^':3} # dictionary having priorities of Operators

def infixToPostfix(expression):

stack = [] # initialization of empty stack

output = ''

for character in expression:


if character not in Operators: # if an operand append in postfix expression

output+= character

elif character=='(': # else Operators push onto stack

stack.append('(')

elif character==')':

while stack and stack[-1]!= '(':

output+=stack.pop()

stack.pop()

else:

while stack and stack[-1]!='(' and Priority[character]<=Priority[stack[-1]]:

output+=stack.pop()

stack.append(character)

while stack:

output+=stack.pop()

return output

expression = input('Enter infix expression ')

print('infix notation: ',expression)

print('postfix notation: ',infixToPostfix(expression))


Output:
Enter infix expression m*n+(p-q)+r
infix notation: m*n+(p-q)+r
postfix notation: mn*pq-+r+

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.

Basic Operations of Queue


A queue is an object (an abstract data structure - ADT) that allows the following operations:

 Enqueue: Add an element to the end of the queue


 Dequeue: Remove an element from the front of the queue
 IsEmpty: Check if the queue is empty
 IsFull: Check if the queue is full
 Peek: Get the value of the front of the queue without removing it

Working of Queue

Queue operations work as follows:

 two pointers FRONT and REAR


 FRONT track the first element of the queue
 REAR track the last element of the queue
 initially, set value of FRONT and REAR to -1

Enqueue Operation

 check if the queue is full

 for the first element, set the value of FRONT to 0


 increase the REAR index by 1
 add the new element in the position pointed to by REAR
Dequeue Operation

 check if the queue is empty

 return the value pointed by FRONT


 increase the FRONT index by 1
 for the last element, reset the values of FRONT and REAR to -1
Example Program:
class Queue:
def __init__(self):
self.queue = []
def enqueue(self, item):
self.queue.append(item)
def dequeue(self):
if len(self.queue) < 1:
return None
return self.queue.pop(0)
def display(self):
print(self.queue)
def size(self):
return len(self.queue)
q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
q.enqueue(4)
q.enqueue(5)
q.display()
q.dequeue()
print("After removing an element")
q.display()
Output

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 widely used as waiting lists for a single shared resource like printer, disk,
CPU.
2. Queues are used in asynchronous transfer of data (where data is not being transferred
at the same rate between two processes) for eg. pipes, file IO, sockets.
3. Queues are used as buffers in most of the applications like MP3 media player, CD
player, etc.
4. Queue is used to maintain the play list in media players in order to add and remove
the songs from the play-list.
5. Queues are used in operating systems for handling interrupts.

Complexity
Data Time Complexity Space
Structur Compleity
e

Average Worst Worst

Access Searc Insertio Deletio Acces Searc Insertio Deletio


h n n s h n n

Queue θ(n) θ(n) θ(1) θ(1) O(n) O(n) O(1) O(1) O(n)

DOUBLE ENDED QUEUES


 The deque stands for Double Ended Queue. Deque is a linear data structure where the
insertion and deletion operations are performed from both ends.
 We can say that deque is a generalized version of the queue.

REPRESENTATION OF DEQUE

TYPES OF DEQUE
There are two types of deque :-
o Input restricted queue
o Output restricted queue

Input restricted Queue

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

Output restricted Queue


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

Operations performed on deque

There are the following operations that can be applied on a deque -

o Insertion at front
o Insertion at rear
o Deletion at front
o Deletion at rear

Insertion at the front end

In this operation, the element is inserted from the front end of the queue. Before
implementing the operation, we first have to check whether the queue is full or not. If the
queue is not full, then the element can be inserted from the front end by using the below
conditions -

o If the queue is empty, both rear and front are initialized with 0. Now, both will point
to the first element.
o Otherwise, check the position of the front if the front is less than 1 (front < 1), then
reinitialize it by front = n - 1, i.e., the last index of the array.

Insertion at the rear end

In this operation, the element is inserted from the rear end of the queue. Before implementing
the operation, we first have to check again whether the queue is full or not. If the queue is not
full, then the element can be inserted from the rear end by using the below conditions -

o If the queue is empty, both rear and front are initialized with 0. Now, both will point
to the first element.
o Otherwise, increment the rear by 1. If the rear is at last index (or size - 1), then instead
of increasing it by 1, we have to make it equal to 0.

Deletion at the front end

In this operation, the element is deleted from the front end of the queue. Before implementing
the operation, we first have to check whether the queue is empty or not.

If the queue is empty, i.e., front = -1, it is the underflow condition, and we cannot perform the
deletion. If the queue is not full, then the element can be inserted from the front end by using
the below conditions -

If the deque has only one element, set rear = -1 and front = -1.
Else if front is at end (that means front = size - 1), set front = 0.

Else increment the front by 1, (i.e., front = front + 1).

Deletion at the rear end

In this operation, the element is deleted from the rear end of the queue. Before implementing
the operation, we first have to check whether the queue is empty or not.

If the queue is empty, i.e., front = -1, it is the underflow condition, and we cannot perform the
deletion.

If the deque has only one element, set rear = -1 and front = -1.

If rear = 0 (rear is at front), then set rear = n - 1.

Else, decrement the rear by 1 (or, rear = rear -1).

Check empty

This operation is performed to check whether the deque is empty or not. If front = -1, it
means that the deque is empty.

Check full
This operation is performed to check whether the deque is full or not. If front = rear + 1, or
front = 0 and rear = n - 1 it means that the deque is full.

The time complexity of all of the above operations of the deque is O(1), i.e., constant.

Example Program:

import collections
my_deque = collections.deque([10, 20, 30, 40, 50])

my_deque.append(60)
print( "The deque after appending at right: " )
print( my_deque )
my_deque.appendleft(70)
print( "The deque after appending at left: " )
print( my_deque )
my_deque.pop()
print( "The deque after removing from right: " )
print( my_deque )
my_deque.popleft()
print("The deque after removing from left: " )
print( my_deque )

Output:

The deque after appending at right:


deque([10, 20, 30, 40, 50, 60])
The deque after appending at left:
deque([70, 10, 20, 30, 40, 50, 60])
The deque after removing from right:
deque([70, 10, 20, 30, 40, 50])
The deque after removing from left:
deque([10, 20, 30, 40, 50])

Applications of deque
o Deque can be used as both stack and queue, as it supports both operations.
o Deque can be used as a palindrome checker means that if we read the string from both
ends, the string would be the same.

You might also like