Queues and Stacks: Atul Prakash Downey: Chapter 15 and 16
Queues and Stacks: Atul Prakash Downey: Chapter 15 and 16
Queues
Queues occur in real life a lot. Queues at checkout Queues in banks In software systems: Queue of requests at a web servers
Properties of Queues
Queue is a FIFO data structure. First-inrst-out. Basic operations: end of the queue
isEmpty() -> returns true if queue empty Some queues provide additional
operations, such as peeking at the front
Create empty queue boolean Q.isEmpty(): true if queue is empty Q.enqueue(T elem): add element at the end of the list T Q.dequeue(): remove element from the front of the list and returns it.
Design Outline
List a; isEmpty() { if a.size() == 0 return true; else return false; } enqueue(T element) { a.add(element); } dequeue() { T result = a.get(0); a.remove(result); return result; } Queue() { a = new List() }
Queue.java
List.java
Discussion
Lists can do everything that queues can So, why implement a queue class? Why not
directly use lists?
Wrapping
We created lists by using an array and
starting point providing functions such as add(), remove(), get(), size(), etc.
return result
enqueue operation:
a[size] = element; size++. Performance: O(1) But, if array lls up, expensive. all n elements have to be copied to a new, larger array. Cost: O(n) in copying. Think about if you double every time array lls up, what is the average cost per insertion? Suppose array size is 100.
First 100 insertions are cheap (constant time). Next one costs 100 operations. Then, the next 100 will be cheap. Next one costs 200. Then, the next 200 will be cheap, next one costs 400, etc.
One expensive operation makes subsequent ones cheap. Average performance per insert is at most one element copy + one element update. It is still constant time.
deque Operation
save A[0], shift all elements left, and
decrement size
Overall Cost
With array-based lists, adding elements at
the end and removing from the front:
Mini-exercise
Create a stack by wrapping a list. Three
operations:
Discussion
Circular queue
Keep a head pointer to the the front of the queue. Tail pointer to the next empty slot. Best to imagine the array to be CIRCULAR. Position 0 follows 17.
Insertion
We added 15 to the queue. tail advances. Basic code (without error checking for full queue) A[tail] = elem; tail = (tail + 1) % size.
Deletion
head advances on deletion. Basic code without error checking for empty queue: result = A[head]; head = (head + 1) mod size; return result
Wrap around
After some deletions and insertions, we can get the above situation. Queue: 4, 5, 8, ., 12. Arithmetic on head/tail must be mod 18
Empty Queue
What happens when we dequeue the last element from the queue?
Rule: (head == tail) means the queue is empty. Initialization of a circular queue: head = 0; tail = 0 would be a reasonable choice.
Full Queue
What happens on adding to the following
queue?
Note
If the queue contains values, we can't really
use the values to determine whether the queue is empty or full.
Equivalent queues
10
30
40
Rule: if (tail + 1) mod size equals head, then the queue is full.
Discussion
Circular queues, as described, are of
bounded size, limited by array size. queues?