You are on page 1of 41

CMPUT 175 (W21)

Queue,
Bounded Queue, Circular Queue

You should view the vignette:


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

We will discuss the ADT, and learn how to


implement the Queue data structure in
Python.

We will discuss other implementations, such


as bounded Queues and circular Queues.

February 24, 2021 © 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 24, 2021 © Osmar R. Zaïane : University of Alberta 3
Real Life Queues

February 24, 2021 © Osmar R. Zaïane : University of Alberta 4


How Useful Are Queues?
Example applications that use queues:
Printer queue for waiting printing tasks

Computing processes waiting list


Traversing a maze
Keyboard buffer

February 24, 2021 © Osmar R. Zaïane : University of Alberta 5


Queue Example - Printer
The online textbook gives an illustrative example
of using Queues to manage printing tasks in a
computing lab, and provides a simulation.

February 24, 2021 © Osmar R. Zaïane : University of Alberta 6


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 24, 2021 © Osmar R. Zaïane : University of Alberta 7
Queue Example – Searching Maze
The algorithm seems the same as before, 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 24, 2021 © Osmar R. Zaïane : University of Alberta 8


Queue Example – Maze Trace

S
0 11 9 P10
P0
P1
P2
P3
P4
P5
P6
P7
P8
P9
1 3 6

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

February 24, 2021 © Osmar R. Zaïane : University of Alberta 9


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()


enqueue()
dequeue()
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

February 24, 2021 © Osmar R. Zaïane : University of Alberta 10


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 choose 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 24, 2021 © Osmar R. Zaïane : University of Alberta 11


Queue Implementation #1
Assume we choose 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

class Queue: 0 n

def __init__(self): Queue using List


self.items = []

def enqueue(self, item): def isEmpty(self):


self.items.insert(0,item) return self.items == []

def dequeue(self): def size(self):


return self.items.pop() return len(self.items)

February 24, 2021 © Osmar R. Zaïane : University of Alberta 12


Printing Queue Objects
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 24, 2021 © Osmar R. Zaïane : University of Alberta 13


Test Implementation #1
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 24, 2021 © Osmar R. Zaïane : University of Alberta 14
Other Implementation Options
Using a list, we implemented class Queue:
def __init__(self):
the Queue by selecting the self.items = []

rear (tail) to be at position 0. def isEmpty(self):


return self.items == []
enqueue Rear Front dequeue
… def size(self):
n return len(self.items)
0
def enqueue(self, item):
We can opt to instead have self.items.append(item)
the front (head) at position 0.
dequeue Front Rear enqueue def dequeue(self):
… return self.items.pop(0)
0 n
Both implementations provide the
 We only need to rewrite same performance
One shifts elements on enqueue, the
enqueue() and dequeue(). other shifts elements on dequeue
February 24, 2021 © Osmar R. Zaïane : University of Alberta 15
Comparing Implementations
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 a more 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 in the term,


after covering Linked Lists
February 24, 2021 © Osmar R. Zaïane : University of Alberta 16
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 24, 2021 © Osmar R. Zaïane : University of Alberta 17
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-1
Data 1 Data 2 Data 3 Data 4 …

February 24, 2021 © Osmar R. Zaïane : University of Alberta 18


Bounded Queue: ADT
BoundedQueue(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 trying
capacity() to enqueue at capacity,
Return the capacity of the queue
clear()
and dequeue when
Empty the queue. queue is empty
February 24, 2021 © Osmar R. Zaïane : University of Alberta 19
Bounded Queue: Constructor

class BoundedQueue:
# Constructor, which creates a new empty queue:
def __init__(self, capacity):
assert isinstance(capacity, int), ('Error: Type error: %s' % (type(capacity)))
assert capacity > 0, ('Error: Illegal capacity: %d' % (capacity))

self.__items = []
self.__capacity = capacity

isEmpty() size()
items capacity
public private

dequeue()
enqueue()

February 24, 2021 © Osmar R. Zaïane : University of Alberta 20


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 24, 2021 © Osmar R. Zaïane : University of Alberta 21


Bounded Queue:
dequeue() and peek()
# Removes and returns the front-most item in the queue.
# Returns nothing if the queue is empty.
def dequeue(self):
if len(self.__items) <= 0:
raise Exception('Error: Queue is empty')
return self.__items.pop(0)

# 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 24, 2021 © Osmar R. Zaïane : University of Alberta 22


Bounded Queue: isEmpty(),
isFull(), size(), 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):
return len(self.__items)

# Returns the capacity of the queue:


def capacity(self):
return self.__capacity

February 24, 2021 © Osmar R. Zaïane : University of Alberta 23


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) + " ")
return str_exp # Returns a string representation of the object
# bounded queue:
def __repr__(self):
return str(self) + "Max=" + str(self.__capacity)

1 2 3 4 5 Max=100
February 24, 2021 © Osmar R. Zaïane : University of Alberta 24
February 24, 2021 © Osmar R. Zaïane : University of Alberta 25
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
Rear

is why we call it circular


D4

February 24, 2021 © Osmar R. Zaïane : University of Alberta 26


Reminder: 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 24, 2021 © Osmar R. Zaïane : University of Alberta 27


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 24, 2021 © Osmar R. Zaïane : University of Alberta 28


Circular Queue: Sliding Indexes
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
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 24, 2021 © Osmar R. Zaïane : University of Alberta 29


Circular Queue: Sliding Indexes
(continued)
head
tail

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

head
tail

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

February 24, 2021 © Osmar R. Zaïane : University of Alberta 30


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 24, 2021 © Osmar R. Zaïane : University of Alberta 31
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 24, 2021 © Osmar R. Zaïane : University of Alberta 32
Circular Queue: ADT
CircularQueue(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 trying
capacity() to enqueue at capacity,
Return the capacity of the queue
clear()
and dequeue when
Empty the queue. queue is empty
February 24, 2021 © Osmar R. Zaïane : University of Alberta 33
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 24, 2021 © Osmar R. Zaïane : University of Alberta 34


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: We could have avoided
raise Exception('Error: Queue is full') if the __init__() had:
for i in range(self.__capacity):
if len(self.__items) < self.__capacity:
self.__items.append(None)
self.__items.append(item)
else:
self.__items[self.__tail]=item
self.__count +=1 items capacity
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 …


February 24, 2021 © 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) q[39,None,23,18]
© Osmar R. Zaïane : University of Albertaoverwrite a None 36
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 24, 2021 © Osmar R. Zaïane : University of Alberta 37


Circular Queue: isEmpty(),
isFull(), size(), 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 24, 2021 © Osmar R. Zaïane : University of Alberta 38


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 24, 2021 © Osmar R. Zaïane : University of Alberta 39
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 24, 2021 © Osmar R. Zaïane : University of Alberta 40


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 24, 2021 © Osmar R. Zaïane : University of Alberta 41

You might also like