Stack and Queue ADTs - PowerPoint PPT Presentation

1 / 18
About This Presentation
Title:

Stack and Queue ADTs

Description:

Stack and Queue ADTs. In 262 you used the built-in Stack and Queue ... we pop, the bottom of the stack is always rooted at items[0] Linked List Implementation ... – PowerPoint PPT presentation

Number of Views:45
Avg rating:3.0/5.0
Slides: 19
Provided by: foxr
Category:
Tags: adts | queue | rooted | stack

less

Transcript and Presenter's Notes

Title: Stack and Queue ADTs


1
Stack 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

2
Implementation 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
3
Implementation 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
4
Linked 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
5
Implementation 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
6
List 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
7
Queue 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

8
Linked 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)
9
Implementation 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!
10
Enqueueing 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
11
Array-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

12
Circular 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

13
Array 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
14
Some 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

15
Using 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!
16
Comparing 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

17
Using 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)
18
Using 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)
Write a Comment
User Comments (0)
About PowerShow.com