AD Unit 2
AD Unit 2
“Ordered” in this definition means that each element has a position in the list. So the
term “ordered” in this context does not mean that the list elements are sorted by
value. (Of course, we can always choose to sort the elements on the list if we want;
it’s just that keeping the elements sorted is not an inherent property of being a list.)
Each list element must have some data type. In the simple list implementations
discussed in this chapter, all elements of the list are usually assumed to have the same
data type, although there is no conceptual objection to lists whose elements have
differing data types if the application requires it. The operations defined as part of the
list ADT depend on the elemental data type. For example, the list ADT can be used
for lists of integers, lists of characters, lists of payroll records, even lists of lists.
We need some notation to show the contents of a list, so we will use the same angle
bracket notation that is normally used to represent sequences. To be consistent with
standard array indexing, the first position on the list is denoted as 0. Thus, if there
are nn elements in the list, they are given positions 0
through n−1n−1 as ⟨ a0, a1, ..., an−1 ⟩⟨ a0, a1, ..., an−1 ⟩. The subscript indicates an
element’s position within the list. Using this notation, the empty list would appear
as ⟨ ⟩⟨ ⟩.
4.2.2. Collections
There are some properties that lists share with many other data structures (some of
them will be introduced later in this course). Then it’s good habit to extract the most
important common properties into a more general kind of ADT, which we will call
collections.
A collection contains a number of elements, and it supports only two things: we can
inquire how many elements it contains, and we can iterate through all elements, one at
the time (i.e., it is Iterable)
Python
class Collection(Iterable):
def isEmpty(self): """Returns true if the collection is empty."""
def size(self): """Returns the number of elements in this collection."""
Note that this very interface will not be implemented as it is, but instead we will use
this as a base interface that we extend in different ways, e.g., for lists or sets or
priority queues.
What basic operations do we want our lists to support? Our common intuition about
lists tells us that a list should be able to grow and shrink in size as we insert and
remove elements. We should be able to insert and remove elements from anywhere in
the list. We should be able to gain access to any element’s value, either to read it or to
change it. Finally, we should be able to know the size of the list, and to iterate through
the elements in the list – i.e., the list should be a Collection.
Now we can define the ADT for a list object in terms of a set of operations on that
object. We will use an interface to formally define the list ADT. List defines the
member functions that any list implementation inheriting from it must support, along
with their parameters and return types.
True to the notion of an ADT, an interface does not specify how operations are
implemented. Two complete implementations are presented later (array-based lists
and linked lists), both of which use the same list ADT to define their operations. But
they are considerably different in approaches and in their space/time tradeoffs.
The code below presents our list ADT. The comments given with each member
function describe what it is intended to do. However, an explanation of the basic
design should help make this clearer. There are four main operations we want to
support:
add(i,x) to add (insert) an element x, at position i, thus increasing the size of the list
remove(i) to remove the element at position i, thus decreasing the size of the list
Apart from these four, we also want to be able to loop through the list elements in
order (i.e., an iterator over the element
Python
class List(Collection):
def add(self, i, x): """Adds x at position i; where 0 <= i <= size."""
def get(self, i): """Returns the element at position i; where 0 <= i < size."""
def set(self, i, x): """Replaces the value at position i with x; where 0 <= i < size."""
def remove(self, i): """Removes the element at position i; where 0 <= i < size."""
# Note: __iter__() should yield the elements starting from position 0.
The List member functions allow you to build a list with elements in any desired
order, and to access any desired position in the list.
Python arrays:
Array is a container which can hold a fix number of items and these items should be
of the same type. Most of the data structures make use of arrays to implement their
algorithms. Following are the important terms
to understand the concept of Array.
Element− Each item stored in an array is called an element.
Index − Each location of an element in an array has a numerical index, which is
used to identify the
element.
Array Representation
Arrays can be declared in various ways in different languages. Below is an
illustration.
Elements
int array [10] = {10, 20, 30, 40, 50, 60, 70, 80, 85, 90}
Type Name Size Index 0
As per the above illustration, following are the important points to be considered.
Index starts with 0.
Array length is 10 which means it can store 10 elements.
Each element can be accessed via its index. For example, we can fetch an element at
index 6 as 70
Basic Operations
Following are the basic operations supported by an array.
Traverse − print all the array elements one by one.
Insertion − Adds an element at the given index.
Deletion − Deletes an element at the given index.
Search − Searches an element using the given index or by the value.
Update − Updates an element at the given index.
Array is created in Python by importing array module to the python program. Then
the array is declared as
shown below.
from array import *
arrayName=array(typecode, [initializers])
Typecode are the codes that are used to define the type of value the array will hold.
Some common
typecodes used are:
Typecode
Value
b
Represents signed integer of size 1 byte/td>
B
Represents unsigned integer of size 1 byte
c
Represents character of size 1 byte
i
Represents signed integer of size 2 bytes
I
Represents unsigned integer of size 2 bytes
f
Represents floating point of size 4 bytes
d
Represents floating point of size 8 bytes
Creating an array:
from array import *
array1 = array('i', [10,20,30,40,50])
for x in array1:
print(x)
Output:
10
20
30
40
50
Accessing Array Element
We can access each element of an array using the index of the element.
from array import *
array1 = array('i', [10,20,30,40,50])
print (array1[0])
print (array1[2])
Output:RESTART: C:/Users/MRCET/AppData/Local/Programs/Python/Python38-
32/pyyy/arr2.py
10
30
Insertion Operation
Insert operation is 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.
Here, we add a data element at the middle of the array using the python in-built
insert() method.
from array import *
array1 = array('i', [10,20,30,40,50])
array1.insert(1,60)
for x in array1:
print(x)
Output:
10
60
20
30
40
50
>>>
Deletion Operation
Deletion refers to removing an existing element from the array and re-organizing all
elements of an array. Here, we remove a data element at the middle of the array using
the python in-built remove() method.
from array import *
array1 = array('i', [10,20,30,40,50])
array1.remove(40)
for x in array1:
print(x)
Output:
10
20
30
50
Search Operation
You can perform a search for an array element based on its value or its index.
Here, we search a data element using the python in-built index() method.
from array import *
array1 = array('i', [10,20,30,40,50])
print (array1.index(40))
Output:
3
>>>
Update Operation
Update operation refers to updating an existing element from the array at a given
index.
Here, we simply reassign a new value to the desired index we want to update.
from array import *
array1 = array('i', [10,20,30,40,50])
array1[2] = 80
for x in array1:
print(x)
Output:
10
20
80
40
50
Arrays vs List
Both list and array and list are used to store the data in Python. These data structures
allow us to indexing, slicing, and iterating. But they have little dissimilarity with each
other.
Python List
A list is a built-in, linear data structure of Python. It is used to store the data in a
sequence manner. We can perform several operations to list, such as indexing,
iterating, and slicing. The list comes with the following
features.
o The list elements are enclosed with a square bracket, and each element is separated
by a comma (,).
o It is a mutable type which means we can modify the list items after their creation.
o The lists are ordered, which means items are stored in a specific order. We can use
indexing to
access the list element.
o We can store the items of different data types. We can combine strings, integers,
and objects in the
same list.
Below is an example of a list.
Example -
1. list = [31, 60, 19, 12]
2. print(list)
3. print(type(list))
Output:
[[31, 60, 19, 12]
<class 'list'>, 60, 19, 12]
Example - 2
1. # creating a list containing elements
2. # belonging to different data types
3. list1 = [1,"Yash",['a','e']]
4. print(list1)
Output: [1, 'Ya ['a', 'e']]
[1, 'Yash', ['a', 'e']]
In the above list, the first element is an integer; the second is a string and third is a list
of characters.
Array in Python
An array is also a linear data structure that stores the data. It is also ordered,
mutable,and enclosed in square brackets. It can store the non-unique items. But there
are restrictions to store the different data type values.
To work with the Array in Python, we need to import either an array module or a
Numpy.
1. import array as arr
or
2. import numpy as np
Elements are allocated in contiguous memory location that allows us to easy
modification, addition,
deletion, accessing of element. Moreover, we need to specify the data type. Let's
understand the following
example.
Example -
1. import array as arr
2. array_1 = arr.array("i", [31, 60,19, 12])
3. print(array_1)
4. print(type(array_1))
Output:
array('i', [31, 60, 19, 12])
<class 'array.array'>
Example - 2: Using Numpy array
1. import numpy as np
2. array_sample = np.array(["str", 'sunil', 'sachin', 'megha', 'deepti'])
3. print (array_sample)
4. print(type(array_sample))
Output:
['['numbers' 'sunil' 'sachin' 'megha' 'deepti']
<class 'numpy.ndarray'>
We have specified the string type and stored the string value.
Difference between Array and List
Linked Lists:
Linked lists are one of the most commonly used data structures in any programming
language.Linked Lists, on the other hand, are different. Linked lists, do not store data
at contiguous memory locations. For each item in the memory location, linked list
stores value of the item and the reference or pointer to the next item.
One pair of the linked list item and the reference to next item constitutes a node.
The following are different types of linked lists.
Single Linked List
A single linked list is the simplest of all the variants of linked lists. Every node in a
single linked list contains an item and reference to the next item and that's it.
Doubly Linked List
Circular Linked List
# Python program to create a linked list and display its elements.
The program creates a linked list using data items input from the user and displays it.
Solution:
1. Create a class Node with instance variables data and next.
2. Create a class Linked List with instance variables head and last_node.
3. The variable head points to the first element in the linked list while last_node points
to the last.
4. Define methods append and display inside the class Linked List to append data and
display the linked list
respectively.
5. Create an instance of Linked List, append data to it and display the list.
Program:
class Node:
def__init__(self, data):
self.data= data
self.next=None
class LinkedList:
def__init__(self):
self.head=None
self.last_node=None
def append(self, data):
ifself.last_nodeisNone:
self.head= Node(data)
self.last_node=self.head
else:
self.last_node.next= Node(data)
self.last_node=self.last_node.next
def display(self):
current =self.head
while current isnotNone:
print(current.data, end =' ')
current = current.next
a_llist = LinkedList()
n =int(input('How many elements would you like to add? '))
for i inrange(n):
data =int(input('Enter data item: '))
a_llist.append(data)
print('The linked list: ', end ='')
a_llist.display()
Program Explanation
1. An instance of Linked List is created.
2. The user is asked for the number of elements they would like to add. This is stored
in n.
3. Using a loop, data from the user is appended to the linked list n times.
4. The linked list is displayed.
Output:
How many elements would you like to add? 5
Enter data item: 4
Enter data item: 4
Enter data item: 6
Enter data item: 8
Enter data item: 9
The linked list: 4 4 6 8 9
Doubly Linked List
A doubly linked list is a linked data structure that consists of a set of sequentially
linked records called nodes.
Each node contains three fields: two link fields (references to the previous and to the
next node in the sequence of nodes) and one data field.
Advantages of Doubly Linked List
1. Traversal can be done on either side means both in forward as well as backward.
2. Deletion Operation is more efficient if the pointer to delete node is given.
Disadvantages of Linked List
1. Since it requires extra pointer that is the previous pointer to store previous node
reference.
2. After each operation like insertion-deletion, it requires an extra pointer that is a
previous pointer which
needs to be maintained.
So, a typical node in the doubly linked list consists of three fields:
o Data represents the data value stored in the node.
o Previous represents a pointer that points to the previous node.
o Next represents a pointer that points to the next node in the list.
The above picture represents a doubly linked list in which each node has two pointers
to point to previous and next node respectively. Here, node 1 represents the head of
the list. The previous pointer of the head node will always point to NULL. Next
pointer of node one will point to node 2. Node 5 represents the tail of
the list whose previous pointer will point to node 4, and the next will point to NULL.
ALGORITHM:
1. Define a Node class which represents a node in the list. It will have three
properties: data, previous
which will point to the previous node and next which will point to the next node.
802. Define another class for creating a doubly linked list, and it has two nodes: head
and tail. Initially,
head and tail will point to null.
3. addNode() will add node to the list:
o It first checks whether the head is null, then it will insert the node as the head.
o Both head and tail will point to a newly added node.
o Head's previous pointer will point to null and tail's next pointer will point to null.
o If the head is not null, the new node will be inserted at the end of the list such that
new node's
previous pointer will point to tail.
o The new node will become the new tail. Tail's next pointer will point to null.
a. display() will show all the nodes present in the list.
o Define a new node 'current' that will point to the head.
o Print current.data till current points to null.
o Current will point to the next node in the list in each iteration.
PROGRAM:
1. #Represent a node of doubly linked list
2. class Node:
3. def __init__(self,data):
4. self.data = data;
5. self.previous = None;
6. self.next = None;
7.
8. class DoublyLinkedList:
9. #Represent the head and tail of the doubly linked list
10. def __init__(self):
11. self.head = None;
12. self.tail = None;
13.
14. #addNode() will add a node to the list
8115. def addNode(self, data):
16. #Create a new node
17. newNode = Node(data);
18.
19. #If list is empty
20. if(self.head == None):
21. #Both head and tail will point to newNode
22. self.head = self.tail = newNode;
23. #head's previous will point to None
24. self.head.previous = None;
25. #tail's next will point to None, as it is the last node of the list
26. self.tail.next = None;
27. else:
28. #newNode will be added after tail such that tail's next will point to newNode
29. self.tail.next = newNode;
30. #newNode's previous will point to tail
31. newNode.previous = self.tail;
32. #newNode will become new tail
33. self.tail = newNode;
34. #As it is last node, tail's next will point to None
35. self.tail.next = None;
37. #display() will print out the nodes of the list
38. def display(self):
39. #Node current will point to head
40. current = self.head;
41. if(self.head == None):
42. print("List is empty");
43. return;
44. print("Nodes of doubly linked list: ");
45. while(current != None):
46. #Prints each node by incrementing pointer.
47. print(current.data),;
48. current = current.next;
49.
50. dList = DoublyLinkedList();
51. #Add nodes to the list
52. dList.addNode(1);
53. dList.addNode(2);
54. dList.addNode(3);
55. dList.addNode(4);
56. dList.addNode(5);
57.
58. #Displays the nodes present in the list
59. dList.display();
Output:
Nodes of doubly linked list:
12345
Circular Linked List
The circular linked list is a kind of linked list. First thing first, 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. Head will point to
the first element of the list, and tail will point to the last element in the list. In the
simple linked list, all the nodes will point to their next element and tail will point to
null. The circular linked list is the collection of nodes in which tail node also point
back to head node. The diagram shown below depicts a circular linked list. Node A
represents head and node D represents tail. So, in this list, A is pointing to B, B is
pointing to C and C is pointing to D but what makes it circular is that node D is
pointing back to node A.
ALGORITHM:
1. Define a Node class which represents a node in the list. It has two properties data
and next which will point to the next node.
2. Define another class for creating the circular linked list, and it has two nodes: head
and tail. It has
two methods: add() and display() .
3. add() will add the node to the list:
o It first checks whether the head is null, then it will insert the node as the head.
o Both head and tail will point to the newly added node.
o If the head is not null, the new node will be the new tail, and the new tail will point
to the head as it is a circular linked list.
a. display() will show all the nodes present in the list.
o Define a new node 'current' that will point to the head.
o Print current.data till current will points to head
o Current will point to the next node in the list in each iteration.
PROGRAM:
1. #Represents the node of list.
2. class Node:
3. def __init__(self,data):
4. self.data = data;
5. self.next = None;
6.
7. class CreateList:
8. #Declaring head and tail pointer as null.
9. def __init__(self):
10. self.head = Node(None);
11. self.tail = Node(None);
12. self.head.next = self.tail;
13. self.tail.next = self.head;
14.
8415. #This function will add the new node at the end of the list.
16. def add(self,data):
17. newNode = Node(data);
18. #Checks if the list is empty.
19. if self.head.data is None:
20. #If list is empty, both head and tail would point to new node.
21. self.head = newNode;
22. self.tail = newNode;
23. newNode.next = self.head;
24. else:
25. #tail will point to new node.
26. self.tail.next = newNode;
27. #New node will become new tail.
28. self.tail = newNode;
29. #Since, it is circular linked list tail will point to head.
30. self.tail.next = self.head;
31.
32. #Displays all the nodes in the list
33. def display(self):
34. current = self.head;
35. if self.head is None:
36. print("List is empty");
37. return;
38. else:
39. print("Nodes of the circular linked list: ");
40. #Prints each node by incrementing pointer.
41. print(current.data),
42. while(current.next != self.head):
43. current = current.next;
44. print(current.data),
45.
46.
47. class CircularLinkedList:
48. cl = CreateList();
49. #Adds data to the list
50. cl.add(1);
51. cl.add(2);
52. cl.add(3);
53. cl.add(4);
54. #Displays all the nodes present in the list
55. cl.display();
Output:
Nodes of the circular linked list:
1234
1) In web browsers, you might have seen that we can always access the previous and
next URL using the back and forward button. Access to previous and next URL
searched is possible because they are linked using a linked list.
2) The songs in the Music Player are linked to the next and the previous song. We can
play songs either from the starting or the end of the list.
3) In an Image Viewer, the next and the previous images are linked; hence they can be
accessed by the previous and the next button.
Circular linked lists also have good usage. A circular linked list is a linked list in
which the last node points to the head instead of pointing to NULL.
Stacks:
Stack works on the principle of “Last-in, first-out”. Also, the inbuilt functions in
Python make the code short and simple. To add an item to the top of the list, i.e., to
push an item, we use append() function and to pop out an element we use pop()
function.
#Python code to demonstrate Implementing stack using list
stack = ["Amar", "Akbar", "Anthony"]
stack.append("Ram")
stack.append("Iqbal")
print(stack)
print(stack.pop())
print(stack)
print(stack.pop())
print(stack)
Output:
['Amar', 'Akbar', 'Anthony', 'Ram', 'Iqbal']
Iqbal
['Amar', 'Akbar', 'Anthony', 'Ram']
Ram
['Amar', 'Akbar', 'Anthony']
Stack Using Linked List
A stack using a linked list is just a simple linked list with just restrictions that any
element will be added and removed using push and pop respectively. In addition to
that, we also keep top pointer to represent the top
of the stack. This is described in the picture given below.
Stack Operations:
1. push() : Insert the element into linked list nothing but which is the top node of
Stack.
2. pop() : Return top element from the Stack and move the top pointer to the second
node of linked list
or Stack.
3. peek(): Return the top element.
4. display(): Print all element of Stack.
A stack will be empty if the linked list won‟t have any node i.e., when the top pointer
of the linked list will
be null. So, let‟s start by making a function to check whether a stack is empty or not.
IS_EMPTY(S)
if S.top == null
return TRUE
return FALSE
Now, to push any node to the stack (S) - PUSH(S, n), we will first check if the stack is
empty or not. If the
stack is empty, we will make the new node head of the linked list and also point the
top pointer to it.
PUSH(S, n)
if IS_EMPTY(S) //stack is empty
S.head = n //new node is the head of the linked list
S.top = n //new node is the also the top
87If the stack is not empty, we will add the new node at the last of the stack. For that,
we will point next of
the top to the new node - (S.top.next = n) and the make the new node top of the stack
- (S.top = n).
PUSH(S, n)
if IS_EMPTY(S) //stack is empty
...
else
S.top.next = n
S.top = n
PUSH(S, n)
if IS_EMPTY(S) //stack is empty
S.head = n //new node is the head of the linked list
S.top = n //new node is the also the top
else
S.top.next = n
S.top = n
Similarly, to remove a node (pop), we will first check if the stack is empty or not as
we did in the implementation with array.
POP(S)
if IS_EMPTY(S)
Error “Stack Underflow”
In the case when the stack is not empty, we will first store the value in top node in a
temporary variable
because we need to return it after deleting the node.
POP(S)
if IS_EMPTY(S) ..
else
x = S.top.data
Now if the stack has only one node (top and head are same), we will just make both
top and head null.
POP(S)
if IS_EMPTY(S)
...
else
...
if S.top == S.head //only one node
S.top = NULL
S.head = NULL
If the stack has more than one node, we will move to the node previous to the top
node and make the next of point it to null and also point the top to it.
POP(S)
...
...
if S.top == S.head //only one node
...
else
tmp = S.head
while tmp.next != S.top //iterating to the node previous to top
tmp = tmp.next
tmp.next = NULL //making the next of the node null
S.top = tmp //changing the top pointer
We first iterated to the node previous to the top node and then we marked its next to
null - tmp.next =
NULL. After this, we pointed the top pointer to it - S.top = tmp.
At last, we will return the data stored in the temporary variable - return x.
POP(S)
if IS_EMPTY(S)
Error "Stack Underflow"
else
x = S.top.data
if S.top == S.head //only one node
S.top = NULL
S.head = NULL
else
tmp = S.head
while tmp.next != S.top //iterating to the node previous to top
tmp = tmp.next
tmp.next = NULL //making the next of the node null
S.top = tmp //changing the top pointer
return x
Stack using linked list
class Node():
def __init__(self, data):
self.data = data
self.next = None
class Stack():
def __init__(self):
self.head = None
self.top = None
def traversal(s):
temp = s.head #temporary pointer to point to head
a = ''
while temp != None: #iterating over stack
a = a+str(temp.data)+'\t'
temp = temp.next
print(a)
def is_empty(s):
if s.top == None:
return True
return False
def push(s, n):
if is_empty(s): #empty
s.head = n
s.top = n
else:
s.top.next = n
s.top = n
def pop(s):
if is_empty(s):
print("Stack Underflow")
return -1000
else:
x = s.top.data
if s.top == s.head: # only one node
s.top = None
s.head = None
else:
temp = s.head
while temp.next != s.top: #iterating to the last element
temp = temp.next
temp.next = None
del s.top
s.top = temp
return x
if __name__ == '__main__':
s = Stack()
a = Node(10)
b = Node(20)
c = Node(30)
pop(s)
push(s, a)
push(s, b)
push(s, c)
traversal(s)
pop(s)
traversal(s)
Applications of Stack
There are many applications of a stack. Some of them are:
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.
Queue
Similar to stacks, a queue is also an Abstract Data Type or ADT. A queue follows
FIFO (First-in, First out) policy. It is equivalent to the queues in our general life.
For example, a new person enters a queue at the last and the person who is at the front
(who must have entered the queue at first) will be served first. Similar to a queue of
day to day life, in Computer Science also, a new element enters a queue at the last
(tail of the queue) and removal of an element occurs from the front (head of the
queue).
Similar to the stack, we will implement the queue using a linked list as well as with an
array. But let‟s first discuss the operations which are done on a queue.
Enqueue → Enqueue is an operation which adds an element to the queue. As stated
earlier, any new item
enters at the tail of the queue, so Enqueue adds an item to the tail of a queue.
Dequeue → It is similar to the pop operation of stack i.e., it returns and deletes the
front element from the
queue.
isEmpty → It is used to check whether the queue has any element or not.
isFull → It is used to check whether the queue is full or not.
Front → It is similar to the top operation of a stack i.e., it returns the front element of
the queue (but don‟t
delete it).
Before moving forward to code up these operations, let‟s discuss the applications of a
queue.
Applications of Queue
Queues are used in a lot of applications, few of them are:
Queue is used to implement many algorithms like Breadth First Search (BFS), etc.
It can be also used by an operating system when it has to schedule jobs with equal
priority
Customers calling a call center are kept in queues when they wait for someone to
pick up the calls
Queue Using an Array
We will maintain two pointers - tail and head to represent a queue. head will always
point to the oldest element which was added and tail will point where the new
element is going to be added.
To insert any element, we add that element at tail and increase the tail by one to point
to the next element of the array.
Suppose tail is at the last element of the queue and there are empty blocks before head
In this case, our tail will point to the first element of the array and will follow a
circular order.
Initially, the queue will be empty i.e., both head and tail will point to the same
location i.e., at index 1. We
can easily check if a queue is empty or not by checking if head and tail are pointing to
the same location or
not at any time.
IS_EMPTY(Q)
If Q.tail == Q.head
return True
return False
Similarly, we will say that if the head of a queue is 1 more than the tail, the queue is
full.
IS_FULL(Q)
if Q.head = Q.tail+1
return True
Return False
Now, we have to deal with the enqueue and the dequeue operations.
To enqueue any item to the queue, we will first check if the queue is full or not i.e.,
Enqueue(Q, x)
if isFull(Q)
Error “Queue Overflow”
else
…
If the queue is not full, we will add the element to the tail i.e, Q[Q.tail] = x.
While adding the element, it might be possible that we have added the element at the
last of the array and in
this case, the tail will go to the first element of the array.
Otherwise, we will just increase the tail by 1.
Enqueue(Q, x)
if isFull(Q)
Error “Queue Overflow”
else
Q[Q.tail] = x
if Q.tail == Q.size
Q.tail = 1
else
Q.tail = Q.tail+1
To dequeue, we will first check if the queue is empty or not. If the queue is empty,
then we will throw an
error.
Dequeue(Q, x)
if isEmpty(Q)
Error “Queue Underflow”
else
…
To dequeue, we will first store the item which we are going to delete from the queue
in a variable because
we will be returning it at last.
Dequeue(Q, x)
if isEmpty(Q)
Error “Queue Underflow”
else
x = Q[Q.head]
…
Now, we just have to increase the head pointer by 1. And in the case when the head is
at the last element of the array, it will go 1.
Dequeue(Q, x)
if isEmpty(Q)
Error “Queue Underflow”
else
x = Q[Q.head]
if Q.head == Q.size
Q.head = 1
else
Q.head = Q.head+1
return x
class Queue:
def __init__(self, size):
self.head = 1
self.tail = 1
self.Q = [0]*(size)
self.size = size
98def is_empty(self):
if self.tail == self.head:
return True
return False
def is_full(self):
if self.head == self.tail+1:
return True
return False
def enqueue(self, x):
if self.is_full():
print("Queue Overflow")
else:
self.Q[self.tail] = x
if self.tail == self.size:
self.tail = 1
else:
self.tail = self.tail+1
def dequeue(self):
if self.is_empty():
print("Underflow")
else:
x = self.Q[self.head]
if self.head == self.size:
self.head = 1
else:
self.head = self.head+1
return x
def display(self):
i = self.head
while(i < self.tail):
print(self.Q[i])
if(i == self.size):
i=0
i = i+1
if __name__ == '__main__':
q = Queue(10)
q.enqueue(10)
q.enqueue(20)
q.enqueue(30)
q.enqueue(40)
q.enqueue(50)
q.display()
print("")
100q.dequeue()
q.dequeue()
q.display()
print("")
q.enqueue(60)
q.enqueue(70)
q.display()
Queue Using Linked List
As we know that a linked list is a dynamic data structure and we can change the size
of it whenever it is needed. So, we are not going to consider that there is a maximum
size of the queue and thus the queue will never overflow. However, one can set a
maximum size to restrict the linked list from growing more than that size. As told
earlier, we are going to maintain a head and a tail pointer to the queue. In the case of
an empty queue, head will point to NULL.
IS_EMPTY(Q)
if Q.head == null
return True
return False
We will point the head pointer to the first element of the linked list and the tail
pointer to the last element of it as shown in the picture given below.
The enqueue operation simply adds a new element to the last of a linked list.
However, if the queue is empty, we will simply make the new node head and tail of
the queue.
ENQUEUE(Q, n)
if IS_EMPTY(Q)
Q.head = n
Q.tail = n
else
Q.tail.next = n
Q.tail = n
To dequeue, we need to remove the head of the linked list. To do so, we will first
store its data in a variable
because we will return it at last and then point head to its next element.
x = Q.head.data
Q.head = Q.head.next
return x
We will execute the above codes when the queue is not empty. If it is, we will throw
the "Queue Underflow" error.
DEQUEUE(Q, n)
if IS_EMPTY(Q)
Error "Queue Underflow"
else
x = Q.head.data
Q.head = Q.head.next
return x
Queue using Linked List
class Node():
def __init__(self, data):
self.data = data
self.next = None
class Queue():
def __init__(self):
self.head = None
self.tail = None
def traversal(q):
temp = q.head
a=” #temporary pointer to point to head
while temp != None: #iterating over queue
a = a+str(temp.data)+'\t'
temp = temp.next
print(a)
def is_empty(q):
if q.head == None:
return True
return False
def enqueue(q, n):
if is_empty(q): #empty
q.head = n
q.tail = n
else:
q.tail.next = n
q.tail = n
def dequeue(q):
if is_empty(q):
print("Queue Underflow")
return -1000
else:
x = q.head.data
temp = q.head
q.head = q.head.next
del temp
return x
if __name__ == '__main__':
q = Queue()
a = Node(10)
b = Node(20)
c = Node(30)
dequeue(q)
enqueue(q, a)
enqueue(q, b)
enqueue(q, c)
traversal(q)
dequeue(q)
traversal(q)
Double Ended Queue
Double ended queue is a more generalized form of queue data structure which allows
insertion and removal of elements from both the ends, i.e , front and back.
Here we will implement a double ended queue using a circular array. It will have the
following methods:
def __init__(self):
self.queue = []
self.count = 0
def __repr__(self):
str = ""
if self.count == 0:
str += "Double Ended Queue Empty."
return str
str += "Double Ended Queue:\n" + self.queue.__repr__()
return str
self.queue.insert(0, data)
self.count += 1
return
self.queue.append(data)
self.count += 1
return
def remove_start(self):
if self.count == 0:
raise ValueError("Invalid Operation")
x = self.queue.pop(0)
self.count -= 1
return x
def remove_end(self):
if self.count == 0:
raise ValueError("Invalid Operation")
x = self.queue.pop()
self.count -= 1
return x
return self.queue[index]
def size(self):
return self.count
def display(self):
print(self)
return
This is the code for a double-ended queue. There’s many methods, let us discuss them
one by one.
1. The __init__ and __repr__ methods
In the __init__ method, we declare a list named queue that will contain the deque, and
a counter to count the number of items in the list.
In the __repr__ method, we create the string that will be used to print the double-
ended queue.
2. The insert_start and insert_end methods
In the insert_start method, we simply insert the new element at index 0 of the list
queue, and we increment the count of items in the list.
In the insert_end method, we simply append the new item in the list queue, and we
increment the count of items in the list.
3. The remove_start and remove_end methods
In the remove_start method, we check if the list is empty, and if so, then we raise a
ValueError. After that, we pop the item at index 0, decrement the count, and return
the popped item.
In the remove_end method too, we check if the list is empty, and if so, then we raise a
ValueError. After that, we pop the item at the end of the list, decrement the count, and
return the popped item.
4. The get, size, and display methods
In the get method, we return the item at a specified index. If the specified index is out
of range, then we raise a ValueError.
In the size method, we simply return the count that contains the number of items in
the list.
And in the display method, we print the deque.
Let us see the output of the code: