You are on page 1of 44

CMPUT 175

Introduction to Foundations
of Computing
Queue, bounded Queue, Circular Queue

You should view the vignettes:


Queues

Post-test

February 9, 2022 © Osmar R. Zaïane : University of Alberta 1


Objectives
In this lecture we will learn about another
linear structure called Queue.

We will learn about how to implement the


Queue data structure in python.

We will discuss Other implementations such


as bounded Queues and circular Queues.

February 9, 2022 © Osmar R. Zaïane : University of Alberta 2


Queues
Ordered collection where items are added at
one end (called rear or tail) and removed at
the other end (called front or head)
Data 1 Front Rear New Data

dequeue Data 2 Data 3 Data 4 Data 5 enqueue

First in - first out (FIFO)


Add element: enqueue (from rear of queue)
Remove element: dequeue (from front of queue)
is empty
check size (also reset)
February 9, 2022 © Osmar R. Zaïane : University of Alberta 3
Funny Queues

February 9, 2022 © Osmar R. Zaïane : University of Alberta 4


How useful are Queues
We will see some examples:
Printing queue for waiting printing tasks
Computing processes waiting list
Keyboard buffer
Traversing a maze

February 9, 2022 © Osmar R. Zaïane : University of Alberta 5


Queue Example - Maze Algorithm
Like Stacks, Queues can also be used to store
unsearched paths.
Repeat as long as the current square is
S
0 5 4

not null and is not the finish square: 1 2 3

“Visit” the square and mark it as visited. 6 7 8

Enqueue one square on the queue for each 10 11 9


F
unvisited legal move from the current square.
Dequeue the queue into the current square
or bind the current square to null if the
queue is empty.
If the current square is the goal we are
successful, otherwise there is no solution
February 9, 2022 © Osmar R. Zaïane : University of Alberta 6
Queue Example - Searching
The algorithm seems the same so what is the
difference between using a Stack and a Queue?
When a Stack is used, the search goes as deep
along a single path as possible, before trying
another path.
When a Queue is used, the search expands a
frontier of nodes equidistant from the start.
S S
0 5 4 0 11 9

Stack 1 2 3 Queue 1 3 6

Search 6 7 8 Search 2 5 8

10 11 9 4 7 10
F F
February 9, 2022 © Osmar R. Zaïane : University of Alberta 7
Queue Example - Maze
Trace

S
0 11 9 P10
P0
P1
P2
P3
P4
P5
P6
P7
P8
P9
1 3 6
Front
2 5 8 P7
P3
P4
P5
P6 Success!
P10
P2
P4
P5
P6
P7
P8
P9
4 7 10
F P10
P11
P1
P3
P5
P6
P7
P8
P9

Rear

February 9, 2022 © Osmar R. Zaïane : University of Alberta 8


Queue Abstract data Type
Queue()
Create a new queue that is empty.
It needs no parameters and returns an empty queue isEmpty() size()
enqueue(item)
Adds a new item to the rear of the queue. public private
It needs an item and returns nothing dequeue()
dequeue() enqueue()

Remove the front item from the queue


It needs no parameters and returns the item.
The queue is modified.
isEmpty()
Test to see whether the queue is empty
It needs no parameters and returns a Boolean value
size()
Returns the number of items on the queue
It needs no parameters and returns an integer
clear()
Empty the queue.
February 9, 2022 © Osmar R. Zaïane : University of Alberta 9
Queue Implementation in
Python
How to store the elements in the queue and allow
the queue to grow and shrink one element at a
time?
Using a python List
We chose the front (head) and rear (tail) of the queue
to correspond to some fixed end of the list

Implement each and every method as specified in


the Queue ADT (enqueue, dequeue, isEmpty, size)
Implement the class and instance constructor
February 9, 2022 © Osmar R. Zaïane : University of Alberta 10
Implementation
Assuming we chose to have the rear on the
left (position 0) of the list and the front on
the right of a list. enqueue Rear Front dequeue

0 n
class Queue: Queue using List
def __init__(self):
def isEmpty(self):
self._items = []
return self._items == []
def enqueue(self, item):
def size(self):
self._items.insert(0,item)
return len(self._items)
def dequeue(self):
def clear(self):
return self._items.pop()
self._items=[]
February 9, 2022 © Osmar R. Zaïane : University of Alberta 11
Printing the queue
How to display the queue instance?
The queue is implemented as a list and
python knows how to display it.
It is better to define a method to
display the queue. Let’s call it show()

def show(self): def __str__(self):


print (self._items) return str(self._items)

Converts the object into a string

February 9, 2022 © Osmar R. Zaïane : University of Alberta 12


Let’s test it
q=Queue()
[]
q.show()
print (q.isEmpty()) True
q.enqueue("bob") ['bob']
q.show() False
print (q.isEmpty()) ['paul', 'eva', 'bob']
q.enqueue("eva")
3
q.enqueue("paul")
q.show() ['paul', 'eva']
print (q.size()) bob was first in the queue
item=q.dequeue() 2
q.show()
print (item,"was first in the queue")
print (q.size()) It seems to work as designed but
these are very rudimentary tests.
More stringent tests done in
isolation are always required.
February 9, 2022 © Osmar R. Zaïane : University of Alberta 13
What is the ouput?
['paul','eva'] ['green']
from Queue import Queue from Queue import Queue

q=Queue() q= Queue()
q.enqueue("bob") q.enqueue('blue')
q.enqueue("eva") q.enqueue('red')
q.enqueue("paul") while not(q.dequeue() == 'red'):
if (q.dequeue() == 'eva'): q.enqueue('green')
q.dequeue() q.show()
q.show()

February 9, 2022 © Osmar R. Zaïane : University of Alberta 14


Printing Queue
The textbook gives an illustrative example of
use of Queues to manage printing tasks in a
computing lab. and provides a simulation.

February 9, 2022 © Osmar R. Zaïane : University of Alberta 15


Implementation Options
Using a list, we class Queue:
def __init__(self):
implemented the Queue self._items = []
def isEmpty(self):
by selecting the rear (tail) return self._items == []
to be at position 0. def size(self):
enqueue Rear Front return len(self._items)
dequeue

n
def enqueue(self, item):
0
self._items.append(item)
We can opt to have the
front (head) at position 0. def dequeue(self):
dequeue Front Rear enqueue return self._items.pop(0)

0 n Both implementations provide the
We need only to rewrite same performance
One shifts elements on enqueue, the
enqueue() and dequeue(). other shifts elements on dequeue
February 9, 2022 © Osmar R. Zaïane : University of Alberta 16
Comparision
Rear at position 0. Front at position 0.
enqueue Rear Front dequeue dequeue Front Rear enqueue
… …
0 n 0 n

dequeue operation is O(1) dequeue operation is O(n)


enqueue operation is O(n) enqueue operation is O(1)

There is an efficient implementation of the


ADT queue that allows adding and removing
element with O(1): Doubly-Linked-List
We will see this data structure later after
covering Linked Lists
February 9, 2022 © Osmar R. Zaïane : University of Alberta 17
Bounded Queues
A bounded queue is a queue limited to
a fixed number of items.
The capacity is fixed at creation of the
queue and cannot be changed
This is possible when we know a-priori
the maximum size of the queue we
would need.
We can impose the front to be at
position 0 of the list and have an index
for the rear that we slide along the list.
February 9, 2022 © Osmar R. Zaïane : University of Alberta 18
Bounded Queues Visual
This queue of capacity n has 4 elements.
Front is always at position 0
Current Rear is at position 3
Front Rear
0 n
Data 1 Data 2 Data 3 Data 4 …

February 9, 2022 © Osmar R. Zaïane : University of Alberta 19


Bounded Queue ADT
BQueue(capacity)
Create a new queue of size capacity that is empty.
enqueue(item)
Adds a new item item to the rear of the queue (if there is room).
dequeue()
Remove the front item from the queue and return it.
peek()
Return the front item from the queue without removing it
isEmpty()
Test to see whether the queue is empty and return a Boolean value
isFull() ç
Test to see whether the queue reached full capacity and return a
Boolean value
size() We need to raise
Return the number of items on the queue exceptions when
capacity() ç enqueue and reaching
Return the capacity of the queue
clear()
capacity or dequeue
Empty the queue. when queue is empty
February 9, 2022 © Osmar R. Zaïane : University of Alberta 20
Bounded Queue
Constructor
try:
class BoundedQueue: q = BoundedQueue(-2)
# Constructor, which creates a new empty queue: except AssertionError as e:
def __init__(self, capacity): print(e.args[0])
assert isinstance(capacity, int), ('Error: Type error: %s' % (type(capacity)))
assert capacity >0, ('Error: Illegal capacity: %d' % (capacity))

self._items = []
self._capacity = capacity Error: Illegal capacity: -2

isEmpty() size()
items capacity
public private

dequeue()
enqueue()

February 9, 2022 © Osmar R. Zaïane : University of Alberta 21


Bounded Queue enqueue()
# Adds a new item to the back of the queue, and returns nothing:
def enqueue(self, item):
if len(self._items) >= self._capacity:
raise Exception('Error: Queue is full')
self._items.append(item)

0
Data 1 Data 2 Data 3 Data 4 …

items capacity
February 9, 2022 © Osmar R. Zaïane : University of Alberta 22
Bounded Queue dequeue()
and peek()
try:
# Removes and returns the front-most item in the queue.
# Returns nothing if the queue is empty. item = q.dequeue()
def dequeue(self): except Exception as e:
if len(self._items) <= 0: print(e.args)
raise Exception('Error: Queue is empty')
return self._items.pop(0)
('Error: Queue is empty',)
# Returns the front-most item in the queue, and DOES NOT change the queue.
def peek(self):
if len(self._items) < = 0:
raise Exception('Error: Queue is empty')

return self._items[0]

February 9, 2022 © Osmar R. Zaïane : University of Alberta 23


Bounded Queue isEmpty(),
IsFull(), size() and capacity()
# Returns True if the queue is empty, and False otherwise:
def isEmpty(self):
return len(self._items) == 0

# Returns True if the queue is full, and False otherwise:


def isFull(self):
return len(self._items) == self._capacity

# Returns the number of items in the queue:


def size(self): isEmpty() size()
return len(self._items) isFull() clear()
items
capacity
peek()
# Returns the capacity of the queue: capacity()
enqueue()
def capacity(self): dequeue()

return self._capacity
February 9, 2022 © Osmar R. Zaïane : University of Alberta 24
Bounded Queue clear()
and str()
# Removes all items from the queue, and sets the size to 0
# clear() should not change the capacity
def clear(self):
self._items = []

# Returns a string representation of the queue:


def __str__(self):
str_exp = "<"
for item in self._items:
str_exp += (str(item) + " ") # Returns a string representation of the object
return str_exp + "<" # bounded queue:
def __repr__(self):
return str(self) + " Max=" + str(self._capacity)

<1 2 3 4 5 < Max=100


February 9, 2022 © Osmar R. Zaïane : University of Alberta 25
February 9, 2022 © Osmar R. Zaïane : University of Alberta 26
Circular Queues
A circular queue is a bounded queue
but instead of pinning the front (head)
at position 0, both rear and front have
indexes that slide
Front Rear
0 n
… Data 1 Data 2 Data 3 Data 4 …

Front is “chasing” rear D1


D2

modulo the capacity. This Front


D3

is why we call it circular


Rear
D4

February 9, 2022 © Osmar R. Zaïane : University of Alberta 27


Let’s talk about Modulus
The modulo operation finds the
remainder after division of one number
a by another n
a modulo n or a mod n or a % n
Remainder of a/n
a%n is always between 0 and n-1
a%n

0 1 … n-2 n-1

February 9, 2022 © Osmar R. Zaïane : University of Alberta 28
Visualize Modulus
0
Clock is hour modulo 12

0,1,2,3,4,5,6,7,8,9,10,11,0,1,2,3,4,5,6,7,8,9,10,11,0,1,2…

33 % 6 = ? 33 % 6 = 3

February 9, 2022 © Osmar R. Zaïane : University of Alberta 29


Sliding Indexes (slide 1)
head

head
tail

tail
0 1 2 3 4 0 1 2 3 4
D1 D2 D3 enqueue D1 D2 D3 D4
D4
Add at tail then increment tail
ue
que
de D1
head

head
tail

tail
0 1 2 3 4 0 1 2 3 4
D2 D3 D4 enqueue D2 D3 D4 D5
D5
Remove from head then increment head
February 9, 2022 © Osmar R. Zaïane : University of Alberta 30
head
Sliding Indexes (slide 1)
tail

head
tail
0 1 2 3 4 0 1 2 3 4
D2 D3 D4 D5 dequeue D3 D4 D5
D2

ue
q ue
en D6
head

head
tail

tail
0 1 2 3 4 0 1 2 3 4
D6 D3 D4 D5 dequeue D6 D4 D5
D3

February 9, 2022 © Osmar R. Zaïane : University of Alberta 31


Circular Queues - Empty and Full
We leave one empty entry in the Queue.

The condition for an empty Queue is:


head == tail.

tail
head
0 1 2 3 4

The condition for a full Queue is: tail is


one “behind” head. head
tail

0 1 2 3 4
D6 D3 D4 D5
February 9, 2022 © Osmar R. Zaïane : University of Alberta 32
Circular Queues - Empty and Full
We can avoid leaving one empty entry in the Queue
by caching the current size of the queue
The condition for an empty Queue is: the size is 0.
The condition for a full Queue is: the size is equal to
the queue maximum capacity.
When enqueueing the cached size is incremented
When dequeueing the cached size is decremented

head
tail
Dequeue: Enqueue: count 4
remove from head add to tail
0 1 2 3 4
increment head increment tail
D6 D3 D4 D5
February 9, 2022 © Osmar R. Zaïane : University of Alberta 33
Circular Queue ADT
CQueue(capacity)
Create a new queue of size capacity that is empty.
enqueue(item)
Adds a new item item to the rear of the queue (if there is room).
dequeue()
Remove the front item from the queue and return it.
peek()
Return the front item from the queue without removing it
isEmpty()
Test to see whether the queue is empty and return a Boolean value
isFull()
Test to see whether the queue reached full capacity and return a
Boolean value
size() We need to raise
Return the number of items on the queue exceptions when
capacity() enqueue and reaching
Return the capacity of the queue
clear()
capacity or dequeue
Empty the queue. when queue is empty
February 9, 2022 © Osmar R. Zaïane : University of Alberta 34
Circular Queue
Constructor
Different way to
class CircularQueue:
test input
# Constructor, which creates a new empty queue:
parameter
def __init__(self, capacity):
if type(capacity) != int or capacity<=0:
raise Exception (‘Capacity Error')

self._items = []
self._capacity = capacity
self._count=0
self._head=0
self._tail=0 isEmpty() size()
items capacity
public private

dequeue() count head


enqueue()
tail
February 9, 2022 © Osmar R. Zaïane : University of Alberta 35
Circular Queue enqueue()
# Adds a new item to the back of the queue, and returns nothing:
def enqueue(self, item):
if self._count== self._capacity: items capacity
raise Exception('Error: Queue is full') 4
if len(self._items) < self._capacity: count
self._items.append(item) head tail
else:
self._items[self._tail]=item 3 2 1
self._count +=1
self._tail=(self._tail +1) % self._capacity [39,None,23,18]
q=CircularQueue(4) qà[] instance created
q.enqueue(45) qà[45] append
q.enqueue(67) qà[45,67] append
q.enqueue(23) qà[45, 67, 23] append
q.dequeue() qà[None,67,23] overwrite with None
q.enqueue(18) qà[None,67,23,18] append
q.dequeue() qà[None,None,23,18] overwrite with None
q.enqueue(39)
February 9, 2022 qà[39,None,23,18]
© Osmar R. Zaïane : University of Albertaoverwrite a None 36
Circular Queue enqueue()
# Adds a new item to the back of the queue, and returns nothing:
def enqueue(self, item): We could have avoided
if self._count== self._capacity: it if the constructor had:
raise Exception('Error: Queue is full') for i in range(0, capacity):
if len(self._items) < self._capacity:
self._items.append(item) self.items.append(None)
else:
self._items[self._tail]=item
items capacity
self._count +=1
self._tail=(self._tail +1) % self._capacity count
head tail
0 Data 1 Data 2 Data 3 Data 4

0 … Data a Data b Data c …


q=CircularQueue(4) qà[None,None,None,None]
February 9, 2022 © Osmar R. Zaïane : University of Alberta 37
Circular Queue dequeue() & peek()
# Removes and returns the front-most item in the queue.
# Returns nothing if the queue is empty.
def dequeue(self):
if self._count == 0:
raise Exception('Error: Queue is empty')
item = self._items[self._head]
self._items[self._head] = None
self._count -=1
self._head = (self._head+1) % self._capacity
return item

# Returns the front-most item in the queue, and DOES NOT change the queue.
def peek(self):
if self._count == 0:
raise Exception('Error: Queue is empty')

return self._items[self._head]
February 9, 2022 © Osmar R. Zaïane : University of Alberta 38
Circular Queue isEmpty(),
IsFull(), size() and capacity()
# Returns True if the queue is empty, and False otherwise:
def isEmpty(self):
return self._count == 0

# Returns True if the queue is full, and False otherwise:


def isFull(self):
return self._count == self._capacity

# Returns the number of items in the queue:


def size(self):
return self._count

# Returns the capacity of the queue:


def capacity(self):
return self._capacity
February 9, 2022 © Osmar R. Zaïane : University of Alberta 39
Circular Queue clear() & str()
# Removes all items from the queue, and sets the size to 0
# clear() should not change the capacity
def clear(self):
self._items = []
self._count=0
self._head=0
self._tail=0

# Returns a string representation of the queue:


def __str__(self):
str_exp = "]"
i=self._head
for j in range(self._count):
str_exp += str(self._items[i]) + " "
i=(i+1) % self._capacity
return str_exp + "]"
]2 3 4 5 6 ]
February 9, 2022 © Osmar R. Zaïane : University of Alberta 40
Circular Queue repr()

# # Returns a string representation of the object CircularQueue


def __repr__(self):
return str(self._items) + " H=“ + str(self._head) + " T="+str(self._tail) + " ("
+str(self._count)+"/"+str(self._capacity)+")"

[None, None, 2, 3, 4, 5, 6, None ] H=2 T=7 (5/8)

[8, None, None, 3, 4, 5, 6, 7 ] H=3 T=1 (6/8)

February 9, 2022 © Osmar R. Zaïane : University of Alberta 41


Circular Queue Testing
q = CircularQueue(4)
q.enqueue('a')
print(repr(q)) ['a'] H=0 T=1 (1/4)
q.enqueue('b')
q.enqueue('c')
print(repr(q)) ['a', 'b', 'c'] H=0 T=3 (3/4)
q.enqueue('d')
print(repr(q)) ['a', 'b', 'c', 'd'] H=0 T=0 (4/4)
try:
q.enqueue('e')
except Exception as e: ('Error: Queue is full',)
print(e.args)
q.dequeue()
print(repr(q)) [None, 'b', 'c', 'd'] H=1 T=0 (3/4)
q.dequeue()
q.dequeue()
q.dequeue()
[None, None, None, None] H=0 T=0 (0/4)
print(repr(q))
try:
q.dequeue()
except Exception as e:
print(e.args)
February 9, 2022 ('Error: Queue is empty',)
© Osmar R. Zaïane : University of Alberta 42
January 9,
Purpose of __str__ and __repr__
Both are used to represent an object
__str__ returns the informal string
representation of an instance
__str__ is called by the built-in function
str() and by a print statement
__repr__ returns an official string
representation of an instance
__repr__ is called by the built-in
function repr()
February 9, 2022 © Osmar R. Zaïane : University of Alberta 43
Double ended Queue = Deque
Also written Dequeue (head-tail linked list)
Addition of elements can be done on both
ends and deletion can be done on both ends
Can be input-restricted or output-restricted
append() and appendLeft() to add
pop() and popLeft() to remove
peek() and peekLeft() to see first available
Use eg. Scheduling jobs in multithread
processor (job stealing)
February 9, 2022 © Osmar R. Zaïane : University of Alberta 44

You might also like