You are on page 1of 79

STACK and QUEUE

Objectives
• Introduction • Implementations
– Define a stack • Array-based
– Define a queue implementations
• Operations • Applications
– Stack operations – Stack applications
– Queue operations – Queue applications
• Java interfaces
– PureStack interface
– PureQueue interface
What is a stack?
• A stack is a finite sequence of elements in which
the only element that can be removed is the
element that was most recently inserted.

• A stack is a data structure that keeps objects in Last-In-First-


Out (LIFO) order
– Objects are added to the top of the stack
– Only the top of the stack can be accessed

• Visualize this like a stack of paper (or plates)


Operations on a Stack
Operation Description

push Adds an element to the top of the stack

pop Removes an element from the top of the stack

peek Examines the element at the top of the stack

isEMpty Determines whether the stack is emkpty

size Determines the number of elements in the stack

• It is not legal to access any element other than


the one that is the top of the stack!
Push and Pop
• Primary operations: Push and Pop
• Push
– Add an element to the top of the stack
• Pop
– Remove the element at the top of the stack
empty stack push an element push another pop

top
B
top top
A A A
top
Stack Operations
Assume a simple stack for integers.
Stack s = new Stack();
s.push(12);
s.push(4);
s.push( s.top() + 2 );
s.pop()
s.push( s.top() );
//what are contents of stack?

6
Stack Operations :

push operation pop operation


j avaxc cxav a j

c c print out contents of


stack in reverse order.
x x
a a
v v
a a
j j
Interface
• A list of abstract methods and constants
– must be public
– constants must be declared as final static

• Abstract method is a method that does not have


an implementation, i.e. it just consists of the
header of the method:

return type method name (parameter list)


The PureStack Interface
public interface PureStack<E>
{
/**
* Determines the number of elements in this
* PureStack object.
*
* @return the number of elements in this
* PureStack object.
*
*/
int size();
/**
* Determines if this PureStack object has no elements.
*
* @return true – if this PureStack object has no
* elements; otherwise, return false.
*
*/
boolean isEmpty();

/**
* Inserts a specified element on the top of this
* PureStack object.
*
* @param element – the element to be pushed.
*
*/
void push (E element);
/**
* Removes the top element from this PureStack object.
*
* @return – the element removed.
* @throws NoSuchElementException – if this PureStack
* object is empty.
*/
E pop();

/**
* Returns the top element on this PureStack object.
*
* @return – the element returned.
* @throws NoSuchElementException – if this PureStack
* object is empty.
*/
E peek();

} // interface PureStack
Generic Types
What is this <E> in the interface definition?
• It represents a generic type
– For generality, we can define a class ( for interface) based
on a generic type rather than as actual type
– Example: a Stack for object of type E
• The actual type is known only when an application
program creates an object of that class
• We can then create stacks of different types:
– stack<int> stackOfIntegers = new Stack<int>();
– stack<String> stackOfString = new Stack<String>();
Implementing an Interface
One or more classes can implement an
interface, perhaps differently
• A class implements the interface by providing
the implementations (bodies) for each of the
abstract methods
• Uses the reserved word implements followed
by interface name
Stack implementation issues
• What do we need to implement a stack?

– A data structure (container) to hold the data


elements

– Something to indicate the top of the stack


Implementation of Stacks
• Any list implementation could be used to
implement a stack
– Arrays (static: the size of stack is given initially)
– Linked lists (dynamic: never become full)

• We will explore implementation based on


array
Array Implementation of a Stack
• The container will be an array to hold the data
elements
– Data elements are kept contiguously at one end of the
array
• The top of the stack will be indicated by its
position in the array (index)
– Let’s assume that the bottom of the stack is at index 0
– The top of the stack will be represented by an integer
variable that is the index on the next available slot in
the array
Array Implementation of a Stack
A stack s with 4 elements
0 1 2 3 4 5 6
……..

s stack

4
top

After pushing an element


0 1 2 3 4 5 6
……..

s stack

5
top
Array Implementation of a Stack
After popping one element
0 1 2 3 4 5 6
……..

s stack

4
top

After popping a second element


0 1 2 3 4 5 6
……..

s stack

3
top
The ArrayPureStack Class
• The class ArrayPureStack implements the PureStack
interface:
Public class ArrayPureStack <E> implements PureStack <E>

• Attributes (instance variables):


private E[] stack; //the container for the data
private int size; // indicates the next open slot
The ArrayPureStack Class
• The array variable stack holds reference to
objects
– Their type is determined when the stack is
instantiated
• The integer variable size represents the index of
the next available slot in the array
Example:
– initially size = 0, the first element is store at index 0
– after the insertion, the size = 1, so the second element is
stored at index 1.
The ArrayPureStack Class
ArrayPureStack Constructors:
• Create an empty stack using default capacity
public ArrayPureStack()
{ stack = (E[]) new Object[DEFAULT_CAPACITY];
size = 0; }

• Create an empty stack using the specified capacity


public ArrayPureStack()
{ stack = (E[]) new Object[initial_CAPACITY];
size = 0;}
Example of using Constructor to create a
Stack of numbers

ArrayPureStack<Integer> s = new ArrayPureStack<Integer>(5);

? ? ? ? ?
s stack 0 1 2 3 4

size
Example: the same ArrayPureStack object after
four items have been pushed on

s.push(41);
s.push(56);
s.push(32); 41 56 32 17 ?
s.push(17); s stack 0 1 2 3 4
4
size
Example: the same ArrayPureStack object after
two items have been pop out

s.pop();
s.pop();
41 56 ? ? ?
s stack 0 1 2 3 4
2
size
The ArrayPureStack Class
The push() operation
• Adds the specified element to the top of the stack,
expanding the capacity of the stack array if necessary
public void push(E element)
{ if (size == stack.length)
expandCapacity();

stack[size++]=element;
}
Managing Capacity
• An array has a particular number of cell when it is
created (its capacity), so array’s capacity is also
the stack’s capacity
• What happens when we want to push a bew
element onto a stack that is full?
– The push method could throw an exception
– It could return status indicator (i.e. a boolean value
true or false, that indicates whether the push was
successful or not)
– It could automatically expand the capacity of the array.
The ArrayPureStack Class
The expandCapacity() operation
• To create a new array to store the contents of the
stack, with twice the capacity
public void expandCapacity()
{ E[] larger = (E[]) new Object[stack.length * 2];
System.arraycopy(stack,0,larger,0,size);
stack = larger;
}
The ArrayPureStack Class
The pop() operation
• Removes the element at the top of the stack and
returns a reference to it.
public E pop()
{
return data[--size];
}
The ArrayPureStack Class
The size() operation
• Returns the number of elements in the stack
public int size()
{
return size;
}
The ArrayPureStack Class
The peek() operation
• Returns a reference to the element at the top of the
stack, the element is not removed from the stack
public E peek()
{
return data[size-1];
}
Stack Applications
Stack Applications
Used of stack in computing
• Word Processors , editors
– To check expressions or string of text for matching
parentheses / brackets
• i.e. if (a==b)
{ c=(d+e) * f; }
– To implement undo operations
• Keeps track of the most recent operations
Stack Applications cont…
Used of stack in computing
• Stack Calculators
– To convert an infix expression to postfix, to make
evaluation easier
• i.e. Infix expression: a*b+c
postfix expression: ab*c+
– To evaluate postfix expression
Stack Applications cont…
Used of stack in computing
• Call stacks (Runtime stack)
– Used by runtime system when methods are
invoked, for method call/return processing
• i.e. main method, calls method1
method1 call method2
method2 returns …
– Hold “call frame” which containing local variables,
parameters, etc…
Stack Applications cont…
Used of stack in computing
• Compilers
– To convert infix expressions to postfix, to make
translation of a high-level language such as Java or
C to a lower level language easier
– Focus more on this
Compilers
• Old compilers:
Infix Machine language
– This gets messy because of parentheses.

• Newer compilers:
Infix Postfix Machine language
Infix and Postfix notation
• In infix notation, an operator is placed between its operands.
• In postfix notation, an operator is placed immediately after
its operands. Parentheses are not needed and not used, in
postfix.

Infix Postfix
a+b ab+
a+b*c abc*+
a*b+c ab*c+
(a + b) * c ab+c*
Infix to Postfix Conversion
The idea of converting infix to postfix using a stack:
Example. Convert infix a + b * c to postfix abc*+.
We scan the input stream left to right, output each operand as they are
scanned. The main idea is that when encountering an operator, it is pushed
onto the stack if the stack is empty, or if it has a higher precedence than that
of the stack top. Thus, the following chart demonstrates the snapshots of the
input stream vs. the stack and the output during the conversion process:
next token stack output comments
(Initially) empty none a empty a
always output operand + + a push when stack
empty b + ab always output operand * +
* ab push if higher precedence c + * a b c
always output operand (at end) empty a b c * + pop
everything off stack
Infix to Postfix Conversion cont…
Another example: Convert infix a*(b + c)/d to postfix abc+*d/.
next token stack output comments
(Initially) empty none a empty a
output operand * * a push operator if stack
empty ( *( a always push ( b *( ab
output operand + *(+ ab push operator if stack
top ( c *(+ abc output operand ) * abc+
pop all operators until ( / / abc+* pop *,
push / d / abc+*d output operand (at
end) empty abc+*d/ pop everything off stack
Note that the token ( is pushed onto stack when scanned; once it is in the
stack all operators are considered with a higher precedence against (. Also,
we need to resolve operators with left or right-associative properties. For
example, a+b+c means (a+b)+c but a^b^c means a^(b^c).
Infix to Postfix Conversion cont…
We define the precedence levels of various operators including the
parentheses, distinguishing whether the operators are currently inside the
stack or they are incoming tokens.
operator tokens in-stack precedence in-coming precedence
(ISP) (ICP) ) (N/A)
(N/A) ^ 3 4 *, / 2
2 +, – 1 1
( 0 4 $ –1
(N/A)
The idea is that when encountering an in-coming operator, pop all operators
in the stack that have a higher or equal ISP than the ICP of the new operator,
then push the new operator onto the stack. The initial stack is marked with a
“bottom marker” $ with a –1 precedence, which serves as a control symbol.
Infix to Postfix Conversion cont…
Rules for converting the infix string:
Starting from the left hand end, inspect each character of the string
1. if it’s an operand – append it to the postfix string
2. if it’s a ‘(‘ – push it on the stack
3. if it’s an operator – if the stack is empty, push it on the stack else pop operators of
greater or equal precedence and append them to the postfix string, stopping when
a ‘(‘ is reached, an operator of lower precedence is reached, or the stack is empty;
then push the operator on the stack
4. if it’s a ‘)’ – pop operators off the stack, appending them to the postfix string, until
a ‘(‘ is encountered and pop the ‘(‘ off the stack
5. when the end of the infix string is reached – pop any remaining operators off the
stack and append them to the postfix string
Infix to Postfix Conversion (continued)

An Example: 7-(2*3+5)*(8-4/2)  723*5+842/-*-


Remaining Infix String char Stack Postfix String Rule Used
7-(2*3+5)*(8-4/2) empty null
-(2*3+5)*(8-4/2) empty 7 1
(2*3+5)*(8-4/2) - 7 3
2*3+5)*(8-4/2) -( 7 2
*3+5)*(8-4/2) -( 72 1
3+5)*(8-4/2) -(* 72 3
+5)*(8-4/2) -(* 723 3
5)*(8-4/2) -(+ 723* 3
)*(8-4/2) -(+ 723*5 1
*(8-4/2) - 723*5+ 4
(8-4/2) -* 723*5+ 3
8-4/2) -*( 723*5+ 2
-4/2) -*( 723*5+8 1
4/2) -*(- 723*5+8 3
/2) -*(- 723*5+84 1
2) -*(-/ 723*5+84 3
) -*(-/ 723*5+842 1
null empty 723*5+842/-*- 4&5
Infix to Postfix Conversion Algorithm

create a stack and push the bottom-marker $ onto stack


perform the following steps forever (until exit out of it) set x =
nextToken(E) if x = end-of-input
pop all operators off the stack and output
(except the marker $) exit out of the loop
else if x = operand, output x else if
x = ‘)’ pop all operators off stack and
output each until ‘(‘; pop ‘(‘ off but don’t output it
else pop all operators off stack as long as their ISP
 ICP(x) push x onto stack
Balanced Symbol Checking
• In processing programs and working with
computer languages there are many instances
when symbols must be balanced
{},[],()

A stack is useful for checking symbol balance.


When a closing symbol is found it must match the
most recent opening symbol of the same type.
Balancing Symbols
openers [ ( { and closers ] ) }

public class A
public static void main(String[ args
System.out PRINTln( "Hello" );
for( int j = 0; j < 6 j++ ) j++
doub x = 0.0;
inTeger j = 0;
System.out.println( "Goodbye" );
}
}
Java says 2 errors, but how many can you find?
A.java:1: '{' expected.
A.java:12: Unbalanced parentheses
2 errors
Checks Balanced Symbols First
– Java's compiler apparently first checks to see if
certain symbols are balanced [] {} ()
– It ignores others errors by making a run over the
entire source code just looking for unbalanced
symbols
– If it finds none, it will begin a new pass
• starts reading character by character again
– Fix the unbalanced errors of the previous slide one
by one to observe this behavior
• it probably uses a Stack and an algorithm like this
Algorithm for Balanced Symbol Checking
• Make an empty stack
• read symbols until end of file
– if the symbol is an opening symbol push it onto the
stack
– if it is a closing symbol do the following
• if the stack is empty report an error
• otherwise pop the stack. If the symbol popped does not
match the closing symbol report an error
• At the end of the file if the stack is not empty
report an error
Example

Process these characters, which represent only the openers


and closers in a short Java program: {{([])}}
As the first four characters are read — all openers —push
each onto the stack

[
(
{
{
Pop the first closer
Do they match?
The next symbol read is a closer: ']'.
Pop '[' and compare to ']'.
Since they match, no error would be reported. The
stack would now look like this:

(
{ Still need to process
) } }
{
( matches )
Then ' )' is found, so pop the stack
Since the top symbol '(' matches the closer ')', no
error needs to be reported.
The stack now has two opening curly brakets

{
{ Still need to process
} }
Pop last two. They match.
All is okay
– The two remaining closing curly brakets would
cause the two matching openers to be popped
with no errors
– It is the last in first out nature of stacks that allows
the first '{' to be associated with the last closer '}'.
When do errors occur?
¨ If a closer is found and the stack is empty, you could
also report an error.
- For example with}}, where the opening { was not
incorrectly typed, the stack has no openers.
¨ If you process all symbols and the stack is not
empty, an error should be reported,
- In the case of {{([])} there is a missing right curly
brace. Symbols are processed without error.
- Except at the end, the stack should be empty. It is not and
an error gets reported.
Evaluating postfix expressions

– Stacks set up the evaluation of expressions.


4 + 8 / 2 is different if evaluated left to right.
– Evaluating "infix" expressions is not easy.
• So compilers convert what we write in infix into the
equivalent postfix expression.
– The expression 2 plus 2 in postfix 2 2 +
– Postfix of 3-4-5*3 is
3 4 - 5 3 * -
Evaluation of Postfix Expressions
• Easy to do with a stack
• given a proper postfix expression:
– get the next token
– if it is an operand push it onto the stack
– else if it is an operator
• pop the stack for the right hand operand
• pop the stack for the left hand operand
• apply the operator to the two operands
• push the result onto the stack
– when the expression has been exhausted the result is
the top (and only element) of the stack
Evaluate 3 4 - 5 3 * -
Found operand so push
Found operand so push
Found operator so pop two values,
apply operand, and push the result

4
s.push(3); 3
s.push(4);
right = s.pop(); // found operator - so pop
left = s.pop();
s.push(left - right); -1
3 - 4
– The stack now has one value -1
– The remainder of the expression: 5 3 * -
Continue with 5 3 * -
Found operand so push
Found operand so push
Found operator so pop two values,
apply operand, and push the result

3
s.push(5); 5
s.push(3);
-1
right = s.pop();
left = s.pop();
s.push(left * right); 15
5 * 3 -1
– The Stack has 2 values
– Only one token remains
Continue with -
15

-1

left = s.pop(); // found operator -


right = s.pop();
s.push(left - right);
-1 - 15 -16

– The expression has been processed.


– The value at the top of the stack is the value of the
expression is -16
– Now evaluate 2 3 4 * 5 * -
Queue
Queue
• A queue is a finite sequence of elements in which:
–  Insertion occurs only at the back;
– Deletion occurs only at the front.
• Queues provide First In First Out (FIFO) access to
elements could also say Last In Last Out (LILO)

Can visualize a queue as a line of


customers waiting for service

The next person to be served is the


one who has waited the longest

New elements are placed at the end


of the line
A Conceptual View of a Queue

Rear of Queue Front of Queue


(or Tail) (or Head)

Adding an Element Removing an Element

61
Queue Operations
1. Enqueue : Add an element to the tail of the queue
2. Dequeue : Remove the element at the head of the queue
3. Peek : Look at but do not remove the element at the head
of the queue
Note: These are the standard queue operations. Java 1.5 has a queue
interface which has operations with different names.
Enqueue and Dequeue
• Primary queue operations: Enqueue and Dequeue
• Like check-out lines in a store, a queue has a front and
a back.
• Enqueue
– Insert an element at the back of the queue
• Dequeue
– Remove an element from the front of the queue

Remove Insert
(Dequeue) front rear (Enqueue)
Enqueue and Dequeue
Enqueue “Matt” Enqueue “Andrew” Enqueue “Samira”

Matt Matt Andrew Matt Andrew Samira

Front Back Front Back Front Back

Dequeue

Andrew Samira

Front Back
The PureQueue interface
public interface PureQueue<E>
{
// Returns the number of elements in this PureQueue
// object.
int size( );

// Returns true if this PureQueue object has no


// elements.
boolean isEmpty( );
/**
* Inserts a specified element at the back of this
* PureQueue object.
*
* @param element – the element to be appended.
*/
void enqueue (E element);

/**
* Removes the front element from this PureQueue
* object.
*
* @return – the element removed.
* @throws NoSuchElementException – if this
* PureQueue object is empty.
*/
E dequeue();
/**
* Returns the front element in this PureQueue
* object.
*
* @return – the element returned.
*
* @throws NoSuchElementException – if
* PureQueue object is empty.
*
*/
E front();

} // interface PureQueue
Queue Example
Operation Output Q
enqueue(5) – (5)
enqueue(3) – (5, 3)
dequeue() 5 (3)
enqueue(7) – (3, 7)
dequeue() 3 (7)
front() 7 (7)
dequeue() 7 ()
dequeue() “error” ()
isEmpty() true ()
enqueue(9) – (9)
enqueue(7) – (9, 7)
size() 2 (9, 7)
enqueue(3) – (9, 7, 3)
enqueue(5) – (9, 7, 3, 5)
dequeue() 9 (7, 3, 5)

69
Implementation of Queue
There are many ways to implement a queue:
1. Array
2. Circular queue
3. Linked List
Array implementation of queues cont…
• There are several different algorithms to
implement Enqueue and Dequeue
• Naïve way
– When enqueuing, the front index is always fixed
back
and the back index moves forward in the array.
back
back

3 3 6 3 6 9

front front front


Enqueue(3) Enqueue(6) Enqueue(9)
Array implementation of queues cont…
• Naïve way
– When dequeuing, the element at the front of queue
is removed. Move or shift all the elements after it by
one position. if size were 999, then 998 moves would
be necessary (A bad algorithm for remove
back back back = -1

6 9 9

front front front


Dequeue() Dequeue() Dequeue()
Array implementation of queues cont…

• Better way
– When an item is enqueued, make the back index
move forward.

– When an item is dequeued, the front index moves


by one element towards the back of the queue
(thus removing the front item, so no copying to
neighboring elements is needed).
Array implementation of queues
• A queue is a first in, first out (FIFO) data structure
• This is accomplished by inserting at one end (the back)
and deleting from the other (the front)

0 1 2 3 4 5 6 7
myQueue:
17 23 97 44

front = 0 back = 3

• To insert: put new element in location 4, and set back to 4


• To delete: take element from location 0, and set front to 1
Array implementation of queues cont…
front = 0 back = 3

Initial queue: 17 23 97 44

After insertion: 17 23 97 44 333

After deletion: 23 97 44 333

front = 1 back = 4

• Notice how the array contents “crawl” to the right as elements are inserted and
deleted
• This will be a problem after a while!
Circular arrays
• We can treat the array holding the queue elements as
circular (joined at the ends)

0 1 2 3 4 5 6 7
myQueue:
44 55 11 22 33

back = 1 front = 5

• Elements were added to this queue in the order 11, 22,


33, 44, 55, and will be removed in the same order
• Use: front = (front + 1) % myQueue.length;
and: back = (back + 1) % myQueue.length;
Full and empty queues
• If the queue were to become completely full, it would look
like this:
0 1 2 3 4 5 6 7
myQueue:
44 55 66 77 88 11 22 33

rear = 4 front = 5

• If we were then to remove all eight elements, making the


queue completely empty, it would look like this:
0 1 2 3 4 5 6 7
myQueue:

rear = 4 front = 5
This is a problem! (same) –How to determine it is full or empty
Full and empty queues: solutions
• Solution #1: Keep an additional variable

0 1 2 3 4 5 6 7
myQueue:
44 55 66 77 88 11 22 33

count = 8 rear = 4 front = 5

• Solution #2: Keep a gap between elements: consider the


queue full when it has n-1 elements

0 1 2 3 4 5 6 7
myQueue:
44 55 66 77 11 22 33

rear = 3 front = 5
Applications of Queues
• Operating systems use queues to sequence tasks
waiting for a scarce resource
– Printer queue
– Tasks waiting for CPU

• Simulation of physical systems


uses queues to simulate any
‘first-in first-out’ (FIFO) system
– Supermarket checkouts
– Tollbooths
79

You might also like