Title: Chapter 6: Stacks
1Chapter 6 Stacks
- The first of the new ADTs we will cover in class
- A stack is much like a stack in the real world
- items are placed one on top of each other and you
always access the stack at the top rather than
the middle or bottom - Consider a stack of trays in the cafeteria
- You will push your tray on top of the rest, so
the last tray added, is always on top - You will remove a tray from the top (removing is
called a pop) - Stacks represent a list structure where access is
only at one end, the top, and the accesses form a
pattern called Last-in-first-out (LIFO) - or First-in-last-out
- We will use a stack when we want to ensure that
accesses to the list are done in a LIFO order - This will be useful for performing backtracking
and parsing as we will see in this and later
chapters
2Stack Example
Empty Push 5 Push 8
- Consider a stack of integers
- Start with an empty stack
- Push 5
- Push 8
- Push 13
- Pop gives us 13 with 8 and 5 still on the stack
- Push 12
- Push 6
- Pop gives us 6, with 12, 8 and 5 still on the
stack - Popping the rest gives us, in order, 12, 8 and 5
8 5
5
Push 13 Pop Push 12
13
13 8 5
12 8 5
8 5
3The Stack ADT
- Methods
- Constructor constructs an empty stack
- Pushing a new item onto the stack
- Popping the top item off of the stack and
returning it - Empty determining if the stack is empty or not
- Full determining if the stack has reached its
capacity (optional) - Peek copying and returning the top item off the
top of stack without removing it (optional) - Size returning the number of items currently
pushed on the stack (optional) - Clone (optional)
- Data structure
- A collection that stores items of some type
- Something that tells us where the top of the
stack is - The capacity of the stack (optional)
- The size of the stack (optional)
- Implementation
- Array
- Linked List
4An Array Based Stack of ints
public class IntStack int stack
int top public IntStack(int size)
top -1 stack new
intsize public boolean isEmpty(
) return (top -1)
public boolean isFull( )
return (top stack.length 1)
0 1 2 3 4
Stack of 5 elements goes from 0-4 Full if
top 4 Empty if top -1
top
The stack is an array of ints We start filling
from location 0 and push by adding to top So,
unlike the previous figure, we add to the stack
in a downward fashion Stack is empty if top is
not pointing to the array Stack is full if top
last element (length-1)
5Stack Implementation continued
public void push(int x) if(!isFull( ))
top stacktop
x else System.out.println(Stack
full!) public int size(int x)
return top1
push(5) not full, so add one to top and add 5
there
5
Here, size returns the number of elements
pushed onto the stack We could also have size
return the top-of-stack index value (top)
6Stack Implementation continued
public int pop( ) if(!isEmpty( ))
int temp stacktop
top-- return temp else
System.out.println(stack empty)
return 999 public int
peek( ) if(!isEmpty( ))
return stacktop else return 999
If stack is not empty, return item at top,
and decrement top
Return 5
Use 999 as an error value to indicate that the
stack is empty
Peek is similar to pop except that it doesnt
alter the top of stack
7Stack Implementation continued
public IntStack clone( ) IntStack
copy try copy (IntStack)
super.clone( )
catch(CloneNotSupportedExecption o)
throw new RuntimeException
(Clone not supported)
copy.stack (int ) copy.clone( )
return copy
The clone method works by creating a copy of
all data instances of the IntStack object If
clone is not supported, an exception is thrown
Otherwise, the cloned object is casted as a
stack, and the array portion of the stack is
cloned, then the cloned stack is returned
8Linked List Stack
- The Linked List version of the Stack is similar
in many ways to the Unordered Linked List and
will use the IntNode class defined (see chapter 4
notes) - We have a head pointer called top
- Constructing the Stack simply sets top to null
- If we want to push a new item
- we add at the top (like inserting at the head)
- If we want to remove
- we only allow for the removal of the top item
(like removing from the head) - isEmpty is true if top null
- isFull is never true
- we assume that we will never run out of memory
- peek simply returns the data at the top node
- clone is the same as listCopy
9Stack Implementation
import IntNode public class LinkedStack
IntNode top public LinkedStack( )
top null
public boolean isEmpty( )
return (top null)
public boolean isFull( )
return false
public int peek( ) if(!isEmpty( ) )
return top.getData( )
else return 999
Empty Stack
top
Non-empty Stack
top
6 13 8 9
10Stack Implementation continued
public void push(int x) if (!isFull(
)) IntNode temp new
IntNode(x, top) top temp
else System.out.println(Stack Full!)
public int pop( ) if(!isEmpty( ))
int temp top.getData( )
top top.getLink( )
return temp else return 999
Push 3 create a new IntNode
with data 3 and link top
reset top to point at the new IntNode
3
Pop return top.getData( ) reset
top to point at top.getLink( )
return this value
11Array vs. Linked Implementations
- Recall that there were good reasons to use arrays
for a Bag ADT - binary search and random access
- and good reasons to use a Linked List
- no need to shift elements, no need to change
capacity when adding new elements - This distinction is mostly untrue with Stacks
- adding at one end only means no need to shift
elements - no need for random access or binary search since
we only access the top element - So, the only difference is because of needing to
change the arrays capacity - a fairly minor problem, but we might favor the
linked version because of it
12Stack Complexities
13Stack Applications
- Reversing a word or words
- Balancing parentheses
- Evaluating fully parenthesized expressions
- Translating infix to postfix notation
- Evaluating postfix notation
- Evaluating infix notation using precedence rules
- - we will cover these topics, the others are in
the textbook and you should look them over
yourself
14Printing a File Backwards
- Consider a Stack ADT called StringStack
StringStack ss new StringStack( )
BufferedReader infile String input
infile.readLine( ) while(infile ! null)
ss.push(input) input
infile.readLine( ) String next
while(!ss.isEmpty( ) ) next ss.pop(
) System.out.println(next)
Stack The big bad wolf ate
File The big bad wolf ate the little
lamb.
top
infile
15Parenthesis Matching
- A common requirement of a compiler is the ability
to match up parentheses - Consider the following expression
- a(x 5) y (b4 b5)
- y
- We must make sure that the left and right hand
symbols match up - We can parse through the expression, pushing each
left item on a stack - If we reach a right item, we pop the top of the
stack and compare, if they are the same type, it
is legal, if wrong type then illegal
boolean failed false CharStack cs new
CharStack( ) for(int i0 ilts.length()
!failed i) if(s.charAt(i) (
s.charAt(i)
s.charAt(i) )
cs.push(s.charAt(i)) else if(s.charAt(i)
)) if(cs.pop( ) ! )) failed
true else if(s.charAt(i) )
if(cs.pop( ) ! ) failed true
else if(s.charAt(i) )
if(cs.pop( ) ! ) failed true return
failed
16Evaluating an Expression
- Consider that we want to evaluate a fully
parenthesized expression like - ( (3 (6 (5 1) ) ) (6 / (2 1) ) )
- If we evaluate in a strictly left-to-right
manner, ignoring parentheses and operator
precedence, we get the wrong answer - 3 6 9 5 45 1 44 6 50 / 2 25 1
26 - We must instead evaluate this left-to-right but
not applying an operator until its correct
precendence - parens first, and / next, and last
- How?
- Use two stacks, an operator stack and an operand
stack - Push each operand in order onto the operand stack
- Push each operator in order onto the operator
stack - If a ) is found, pop the top two operands off the
operand stack, pop the top operator off the
operator stack, apply the operator (in opposite
order) to the operands and push the result back
on the operand stack - By the end of the expression, the result is on
the top of the operand stack
17Evaluating Our Example
- Push / onto stack 2 (now /, )
- Push 2 onto stack 1 (now 2, 6, 27)
- Push onto stack 2 (now , /, )
- Push 1 onto stack 1 (now 1, 2, 6, 27)
- Right ), pop and apply to 1, 2 pushing 2 1
onto stack 1 (now 3, 6, 27) - Right ), pop / and apply to 3, 6 pushing 6 / 3
onto stack 1 (now 2, 27) - Right ), pop (stack 1 now empty) and apply to
2, 27, pushing 27 2 onto stack 1 - End of expression, result on top of stack (29)
- ( (3 (6 (5 1) ) ) (6 / (2 1) ) )
- Push 3 onto stack 1
- Push onto stack 2
- Push 6 onto stack 1 (now 6, 3)
- Push onto stack 2 (now , )
- Push 5 onto stack 1 (now 5, 6, 3)
- Push onto stack 2 (now -, , )
- Push 1 onto stack 1 (now 1, 5, 6, 3)
- Right ), pop and apply to 1, 5 pushing 5 1
onto operand 1 (now 4, 6, 3) - Right ), pop and apply to 4, 6 pushing 6 4
onto stack 1 (now 24, 3) - Right ), pop (stack 1 now empty) and apply to
24, 3, pushing 3 24 onto operand stack (now 27) - Push onto stack 2 (now )
- Push 6 onto stack 1 (now 6, 27)
18Expression Evaluation Code
public int readEvaluate(String expr)
IntStack s1 new IntStack( ) CharStack s2
new CharStack( ) char temp
for(int i0iltexpr.length 1 i)
temp expr.charAt(i)
if(Character.isDigit(temp))
s1.push(Integer.parseInt(temp)) else
if(temp temp -
temp temp /)
s2.push(temp) else if(temp
)) evaluateTop(s1, s2) int
answer s1.pop( ) if(s1.isEmpty( )
s2.isEmpty( )) return answer else return 999
public void evaluateTop(IntStack s1,
CharStack s2) int x2
s1.pop( ) int x1 s1.pop( ) char
op s2.pop( ) switch(op)
case s1.push(x1 x2) break
case - s1.push(x1 x2) break
case s1.push(x1 x2) break
case / if(s2!0) s1.push(x1 / x2) else
throw new IllegalArgumentException (division
by zero) break default throw
new IllegalArgumentException (Illegal
Operation)
19Pre- and Post-fix Notations
- We commonly view mathematical expression in infix
notation - operator to be applied is placed in-between the
two operands - example 5 12
- In pre-fix notation, the operator precedes the
operands and in post-fix notation, the operate
succeeds the operands - 5 12
- 5 12
- There are two reasons why these notations are
important - First, some calculators are based on post-fix
notation, it allows the calculator to perform
calculations without needing a memory - Second, expressions written in either of these
notations need no parentheses to come out with
the correct values
20Examples
- Convert the following in-fix notation into both
pre- and post-fix notations - A (B (C / D E F) ) / (G H I)
- Prefix / A B - / C D E F G H I
- This can be evaluated as the division of (the
multiplication of A and (the sum of B and (the
difference of (the division of C and D) and (the
multiplication of E and F) and the sum of (G and
the multiplication of H and I)))) - Postfix A B C D / E F - G H I /
21Using a Stack to Create Postfix
- We will skip creating the pre-fix expression
- But here is a simple algorithm to take a fully
parenthesized infix expression and create the
postfix expression from it - Initialize a CharStack
- Do
- Read the next symbol
- if (symbol () push (onto stack
- else if (symbol is an operand) write the operand
to output - else if (symbol is an operator) push symbol onto
stack - else if (symbol )) then continue to pop
items off of the stack and output them until a
) is reached (do not output the )) - While (there is more expression to read)
- Pop and output the remainder of the stack
(excluding ))
22Example
Output Stack top ? ( ( ( 2
( ( ( ( ( ( (
( ( ( ( ( ( 2 A ( ( ( ( ( - 2 A B
2 A B - ( ( ( ( 2 A B - ( ( ( 2 A B
( ( ( 2 A B 3 ( ( ( 2 A B 3 ( (
( 2 A B 3 C ( ( ( 2 A B - 3 C
- Expression is ( ( (2 (A B) ) 3) C)
- Read (, push onto stack (3 times)
- Read 2, output it
- Read (, push onto stack
- Read , push onto stack
- Read (, push onto stack
- Read A, output it
- Read -, push onto stack
- Read B, push onto stack
- Read ), discard it, pop off stack until ( and
output each item except ( - Read ), discard it, pop off stack until (
- Read , push onto stack
- Read 3, output it
- Read , push onto stack
- Read C, output it
- Input empty, pop all remaining items off of stack
outputting them (other than )) - Answer is 2 A B - 3 C
23Evaluating a Postfix Expression
- Somewhat similar to evaluating a fully
parenthesized infix expression - Have an operand stack
- Parse the input doing the following for each
input item - If item is an operand, push it onto the stack
- If item is an operator
- pop the first two items off the stack
- apply the operator in the order (second item)
operator (first item) - push the resulting value back onto the stack
- Once the input is complete, pop off the top item
of the stack, it is the evaluated expression (and
should be the only thing on the stack)
24Example
- Lets evaluate our previous postfix expression
using the following values - A B C D / E F - G H I /
- A 10 B 2 C 40 D 2 E 2 F 9 G
4 H 2 I 3 - Push 10, Push 2, Push 40, Push 2
- Reach /, pop 2 and 40, apply 40 / 2 and Push
result (20) - Push 2, Push 9
- Reach , pop 9 and 2, apply 9 2, and Push
result (18) - Reach -, pop 18 and 20, apply 20 18, and Push
result (2) - Reach , pop 2, pop 2, apply 2 2, and Push
result (4) - Reach , pop 4, pop 10, apply 10 4, and Push
result (40) - Push 4, Push 2, Push 3
- Reach , pop 3, pop 2, apply 2 3, and Push
result (6) - Reach , pop 6, pop 4, apply 4 6, and Push
result (10) - Reach / , pop 10, pop 40, apply 40 / 10, and Push
result (4) - End of input, pop result, 4 is the answer
- Check to make sure
- A (B (C / D E F) ) / (G H I) 4 for
the above values