Title: Stack and Queue ADTs
1Stack and Queue ADTs
public interface StackInterface public
boolean isEmpty( ) public boolean isFull(
) public void popAll( ) public
void push(Object item) throws StackException
public Object pop( ) throws StackException
public Object peek( ) throws
StackException You might not need isFull( )
since it will not be necessary for a linked list
implementation, but we include it in our
interface for completeness public class
StackException extends RuntimeException
public StackException(String s)
super(s)
- In 262 you used the built-in Stack and Queue
collector classes - Now we consider how to implement them as ADTs
- Implementations will be based on either a(n)
- array
- the queue version will present us with a problem
- linked list
- use a previously implemented List ADT
2Implementation 1 Array
public class Stack1 implements StackInterface
private Object items private int
top private int maxSize public
Stack1( ) maxSize 100
top -1 items new ObjectmaxSize
public Stack1(int size)
maxSize size top -1
items new ObjectmaxSize
public boolean isEmpty( ) return
(top -1) public boolean isFull( )
return (top maxSize 1) public
void popAll( ) items new
ObjectmaxSize top -1
Notice that setting items to a newly
allocated array causes the old array to be
garbage collected since nothing is pointing at it
now In C, you would have to explicitly
deallocate the array first
The stack, being a LIFO structure needs only one
access point, the top, so we use the variable top
to store the index in the array of the top of
the stack
3Implementation Continued
public void push(Object newItem) throws
StackException if(!isFull( ))
itemstop newItem else throw new
StackException(Stack full on push) public
Object pop( ) throws StackException
if(!isEmpty( )) return itemstop--
else throw new StackException(Stack empty on
pop) public Object peek( ) throws
StackException if(!isEmpty( )) return
itemstop else throw new
StackException(Stack empty on peek)
We could implement push to double the array size
if the stack is full so that we never have to
throw a StackException on push but this would
lead to an inefficient push operation
The top of the stack moves from left to right as
we push and right to left as we pop, the bottom
of the stack is always rooted at items0
4Linked List Implementation
public class Stack2 implements StackInterface
private Node top // assume Nodes data
is of type Object, not int public Stack2(
) top null
public boolean isEmpty( ) return
(top null) public boolean
isFull( ) return false
public void push(Object item) throws
StackException Node newNode new
Node(item, top) top newNode
Create a new node which points at the first item
of the stack, now reset top to point at the new
node, thus adding a new Node at the top
5Implementation Continued
public Object pop( ) throws StackException
if(!isEmpty( )) Object temp
top.getData( ) top
top.getNext( ) return temp
else throw new StackException(Stack empty
on pop) public void popAll( )
top null public Object peek( ) throws
StackException if(!isEmpty( ))
return top.getData( ) else throw new
StackException(Stack empty on peak)
temp will point at the Node on top of the stack
reset top to point at the next item on the
stack and return the data field of temp
In C or C, we would have to deallocate each
node one at a time, here Javas garbage collector
takes care of it for us
6List ADT Implementation
Using the List ADT from chapter 5, we implement a
Stack by adding and removing from the List but
always from index 1
public class Stack3 implements StackInterface
private List stack public
Stack3( ) stack new List( )
public boolean isEmpty( ) return
stack.isEmpty( ) public boolean
isFull( ) return
stack.isFull( ) public void
push(Object newItem)
stack.add(1, newItem) public
void popAll( )
stack.removeAll( )
public Object pop( ) throws StackException
if(!stack.isEmpty( ))
Object temp list.get(1)
stack.remove(1)
return temp else throw new
StackException (Stack empty on pop)
public Object peek( ) throws StackException
if(!stack.isEmpty( ))
return stack.get(1) else throw new
StackException (Stack empty on peek)
// end class
7Queue ADT
public interface QueueInterface public
boolean isEmpty( ) public boolean isFull(
) public void dequeueAll( )
public void enqueue(Object item) throws
QueueException public Object dequeue( )
throws QueueException public Object
peek( ) throws StackException Like the
stack, isFull( ) may not be needed in the
interface public class QueueException
extends RuntimeException public
QueueException(String s) super(s)
- Unlike the Stack, the queue has two points of
access - We will refer to them as the rear and front
- some implementations refer to these as the tail
and head - We add (enqueue) at the rear and remove (dequeue)
from the front - For an array implementation, both rear and front
will move - We will have to take care when moving them to the
end of the array
8Linked Implementation
public class Queue1 implements QueueInterface
private Queue front, rear
public Queue1( ) front rear null
public boolean isEmpty( ) return
(front null) public boolean
isFull( ) return false
public void dequeueAll( ) front
rear null
public Object peek( ) throws QueueException
if(!isEmpty( )) return
front.getItem( ) else throw new
QueueException (Empty queue on peek)
9Implementation Continued
public Object dequeue( ) throws QueueException
if(!isEmpty( )) Object
temp front.getItem( ) front
front.getNext( ) if(front null)
rear null return temp
else throw new QueueException(Empty queue on
dequeue) public void enqueue(Object item)
throws QueueException Node newNode new
Node(item, null) if(isEmpty( ))
front newNode rear front
else rear.setNext(newNod
e) rear newNode
Note we need to have throws QueueException
because of the interface even though this
implementation will never throw one when
enqueuing!
10Enqueueing and Dequeueing
- newNode new Node
- (datum, null)
- rear.setNext(newNode)
- rear newNode
For an empty queue, we skip 2 and add front
newNode
- temp front
- front front.getNext( )
- return temp
For a 1-item queue, we skip 2 and add front
null and rear null
11Array-Based Implementation
- The Stack implementation using an array was
straight-forward - Unfortunately, a Queue implementation using an
array has a complication known as rightward drift - As you enqueue items, the rear moves further down
the array (from 0 down to size 1) - The queue is considered full when rear size
1 (that is, the rear of the queue is the end of
the array) - As you dequeue items, front also moves further
down the array - So we might find ourselves with a full queue even
though front gt 0
12Circular Arrays
- From the previous slide, we can see that rear
size 1 does not necessarily mean that the array
is full - The problem is that as rear moves left to right,
it never moves back to the left, but front also
moves left to right so that there might be empty
locations on the left part of the array - We can get around this problem by letting these
pointers move from the end of the array back to
the beginning as needed
- This creates a circular array
- instead of doing rear we now
- rear (rear 1) max
- and instead of doing front we do
- front (front 1) max
- This change will complicate the array
implementation as we need a new way to determine
if a queue is full or not
- We will add a variable, count, to keep track of
the entries in the queue so while this is a
simple fix, it adds a variable to the class
13Array Implementation 1
public void enqueue(Object item) throws
QueueException if(!isFull( ))
back (back 1) maxSize
itemsback item count
else throw new QueueException (Queue full on
enqueue) public Object dequeue( ) throws
QueueException if(!isEmpty( ))
Object temp itemsfront. getItem( )
front (front 1) maxSize
return temp
else throw new QueueException (Queue empty on
dequeue)
public class Queue2 implements QueueInterface
private int front, back, count
private Object items private int
maxSize // constructors omitted to save space
public void dequeueAll(int size)
maxSize size count 0 items new
ObjectsmaxSize front 0 back
maxSize - 1 public boolean isEmpty( )
return (count 0) public boolean
isFull( ) return (count
maxSize)
// peak also omitted, it returns the item at front
14Some Comments
- For this implementation, front and back are
always pointing at the current front and back of
the queue - In a 1-item queue, front back
- In an empty and a full queue, front and back are
such that front is position ahead of back - this is why we cant use (front (back 1
max)) as our condition for a full or empty queue - we resort to the extra variable count instead
- There are other array-based queue implementations
that do not require count - Instead, they set front to point at the position
before the front of the queue, resulting in an
array that wastes 1 element all of the time
15Using the List ADT
public void enqueue(Object item) throws QE
queue.add(queue.getSize( ) 1, item)
public Object dequeue( ) throws QE
if(!isEmpty( )) Object temp
queue.get(1) queue.remove(1)
return temp.getItem( )
else throw new QE(Q empty on
dequeue) public Object peek( ) throws QE
if(!isEmpty( )) return
queue.get(1).getItem( ) else throw new
QE(Q empty on dequeue)
public class Queue4 implements
QueueInterface private List queue
public Queue4( ) queue
new List( ) public boolean
isEmpty( ) return queue.isEmpty( )
public boolean isFull( )
return queue.isFull( )
public void dequeueAll( ) queue new List(
)
Items QueueException and Queue abbreviated to fit
on slide!
16Comparing Implementations
- The array-based implementations for both stack
and queue are wasteful of memory - the queue array is also further complicated by
making it a circular array - The linked implementation
- is efficient in both memory and time usage
- unlike the unsorted/sorted linked list where we
have to physically traverse the list if we want
to access the kth element, we will only want to
access the first or last element - The List ADT implementation
- brings with it the overhead of the rest of the
List ADT - this is invisible to us as Stack users, but is
unnecessary and we wind up underutilizing the
List ADT, so there is no reason to implement our
stacks and queues with the List ADT - The linked implementation is the best for both
the array and queue - we might choose the array implementation for the
stack if the stack is guaranteed to not be
excessively large so that declaring a big enough
array is not wasteful of memory
17Using Stacks
- Stacks are used to implement recursion and
procedure calls (the run-time stack) - we can avoid recursion by using our own stack but
this is unnecessary (some older languages did not
have recursion so this became an essential data
structure) - We can use a stack to evaluate expressions
written in pre-fix and post-fix notation (see
pages 349-360) - We can use a stack to evaluate an expression for
matching delimiters - a compiler will use this to match in a Java
or C program, or a web browser to match the
beginning and ending of html commands - A simple example in using the Stack ADT is in
recognizing a simple language (known as a regular
grammar)
i getNextChar( ) while(i!)
s.push(i) while(!s.empty( )) i
getNextChar( ) if(i!s.pop( )) return
false return true
Example consider the language L ww w
reverse(w), and w may be empty)
18Using Queues
- Queues are primarily used for simulations
- See the example on pages 403-413 if you havent
seen this in 262 - The basic idea of a simulation is as follows
- Initialize all running totals and create a queue
- Enqueue any initial random elements
- While queue is not empty and time limit has not
elapsed - randomly determine if any new element has
appeared to be added to the queue - see if the current item being processed as
completed, if so, remove it, update totals,
dequeue next item (if queue is not empty) and
start processing it - Compute end-of-simulation values (totals,
averages) - Usually we are interested in, on average, how
long an item has to wait in the queue, and how
long it takes to process all items - We may also want to compute the longest and
shortest wait times, etc - We can rerun the simulation by altering the
random elements and possibly add more than 1
processor - for instance, a bank may have multiple tellers
- We may also use multiple queues if we have
different priority lines such as an express line
for VIP customers - Queues and stacks have JCF classes (p. 347-349
398-400)