Stacks: Based On Koffmann and Wolfgang
Stacks: Based On Koffmann and Wolfgang
Carl
Barb push Carl ... Barb
Alex Alex
Chapter 5: Stacks 3
Specification of Stack<E>
As An Abstract Data Type
• Since only the top element of a stack is visible ...
• There are only a few operations
• Need the ability to
• Inspect the top element: peek()
• Retrieve & remove the top element: pop()
• Push a new element on the stack: push(E)
• Test for an empty stack: empty()
Chapter 5: Stacks 4
API for Stack<E> (old)
Chapter 5: Stacks 5
Stack Applications
Chapter 5: Stacks 6
Stack Applications (continued)
Chapter 5: Stacks 7
Palindrome Code
...
Chapter 5: Stacks 8
Palindrome Code (2)
Chapter 5: Stacks 9
Palindrome Code (3)
Chapter 5: Stacks 10
Palindrome Code (4)
Chapter 5: Stacks 11
Stack Applications: Balancing Brackets
Chapter 5: Stacks 12
Balancing Brackets: Overview
• Start with empty stack of currently open brackets
• Process each char of an expression String
• If it is an opener, push it on the stack
• If it is a closer, check it against the top stack element
• If stack empty, or not a matching bracket:
not balanced, so return false
• Otherwise, it matches: pop the opener
• If stack is empty at the end, return true
• If not empty, then some bracket unmatched: false
Chapter 5: Stacks 13
isBalanced Code
Chapter 5: Stacks 14
isBalanced Code: Loop Body
Chapter 5: Stacks 15
isBalanced Code: Helper Routines
private static final String OPEN = “([{“;
private static final String CLOSE = “)]}”;
Chapter 5: Stacks 18
Implementing Stack as Extension of Vector (2)
Top element of
the Stack is at
the highest index
Chapter 5: Stacks 19
Stack Code
public class Stack<E> extends Vector<E> {
public E push (E e) {
add(e);
return e;
}
Chapter 5: Stacks 22
ListStack Code
public class ListStack<E>
implements StackInterface<E> {
public ListStack () {
list = new ArrayList<E>();
// or new Vector<E> or new LinkedList<E>
}
public E push (E e) {
list.add(e);
return e;
}
...
Chapter 5: Stacks 23
ListStack Code (2)
public E peek () {
if (empty())
throw new EmptyStackException();
return list.get(list.size()-1);
}
public E pop () {
if (empty())
throw new EmptyStackException()
return list.remove(list.size()-1);
}
Chapter 5: Stacks 24
Implementing Stack Using an Array
Chapter 5: Stacks 25
Implementing Stack Using an Array (2)
Chapter 5: Stacks 26
ArrayStack Code
public class ArrayStack<E>
implements StackInterface<E> {
public ArrayStack () { }
public E pop () {
if (empty()) throw new EmptyStackException();
return data[top--];
}
public E peek () {
if (empty()) throw new EmptyStackException();
return data[top];
}
Chapter 5: Stacks 28
ArrayStack Code (3)
private reallocate () {
E[] newData = (E[]) new Object[data.length*2];
System.arraycopy(data, 0, newData, 0,
data.length);
data = newData;
}
Chapter 5: Stacks 29
Implementing Stack as a Linked Structure
Chapter 5: Stacks 30
LinkedStack Code
public class LinkedStack<E>
implements StackInterface<E> {
public LinkedStack () { }
public E push (E e) {
top = new Node<E>(e, top);
return e;
}
...
}
Chapter 5: Stacks 31
LinkedStack Code (2)
public E pop () {
if (empty()) throw new EmptyStackException();
E result = top.data;
top = top.next;
return result;
}
public E peek () {
if (empty()) throw new EmptyStackException();
return top.data;
}
Chapter 5: Stacks 32
LinkedStack Code (3)
private static class Node<E> {
private E data;
private Node<E> next;
Chapter 5: Stacks 33
Comparison of Stack Implementations
• Vector is a poor choice: exposes Vector methods
• Likewise, extending other List classes exposes
• Using a List as a component avoid exposing
• LinkedStack operations are O(1)
• But Node objects add much space overhead
• ArrayList component is perhaps easiest
• Still some space overhead (Stack+ArrayList)
• Array as a component best in space and time
• But somewhat harder to implement
Chapter 5: Stacks 34
Additional Stack Applications:
Evaluating Arithmetic Expressions
• Expressions normally written in infix form
• Binary operators appear between their operands:
a+b c*d e*f-g
• A computer normally scans in input order
• One must always compute operand values first
• So easier to evaluate in postfix form:
ab+ cd* ef*g-
Chapter 5: Stacks 35
Postfix Expression Evaluation Examples
Chapter 5: Stacks 36
Advantages of Postfix Form
Chapter 5: Stacks 37
Program: Evaluating Postfix Expressions
Chapter 5: Stacks 38
Program: Evaluating Postfix Expressions (2)
1. Create empty Stack<Integer>
2. For each token of the expression String do:
3. If the token is an Integer (starts with a digit)
4. Push the token’s value on the stack
5. Else if the token is an operator
6. Pop the right (second) operand off the stack
7. Pop the left (first) operand off the stack
8. Evaluate the operation
9. Push the result onto the stack
10. Pop the stack and return the result
Chapter 5: Stacks 39
Program: Evaluating Postfix Expressions (3)
Chapter 5: Stacks 40
Program: Evaluating Postfix Expressions (4)
import java.util.*;
public class PostfixEval {
public static class SyntaxErrorException
extends Exception {
SyntaxErrorException (String msg) {
super(msg);
}
}
Chapter 5: Stacks 43
Program: Evaluating Postfix Expressions (7)
try {
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken();
... loop body ...
}
... after loop ...
} catch (EmptyStackException exc) {
throw new SyntaxErrorException(
“Syntax Error: The stack is empty”);
}
Chapter 5: Stacks 44
Program: Evaluating Postfix Expressions (8)
// loop body: work done for each token
if (Character.isDigit(token.charAt(0))) {
int value = Integer.parseInt(token);
operandStack.push(value);
} else if (isOperator(token.charAt(0))) {
int result = evalOp(token.charAt(0));
operandStack.push(result);
} else {
throw new SyntaxErrorException(
“Invalid character encountered”);
}
Chapter 5: Stacks 45
Program: Evaluating Postfix Expressions (9)
// Work after loop completes
int answer = operandStack.pop();
if (operandStack.empty()) {
return answer;
} else {
throw new SyntaxErrorException(
“Syntax Error: Stack not empty”);
}
Chapter 5: Stacks 46
Converting Infix to Postfix
Chapter 5: Stacks 47
Converting Infix to Postfix (2)
• Analysis:
• Operands are in same order in infix and postfix
• Operators occur later in postfix
• Strategy:
• Send operands straight to output
• Send higher precedence operators first
• If same precedence, send in left to right order
• Hold pending operators on a stack
Chapter 5: Stacks 48
Converting from Infix to Postfix: Example
Chapter 5: Stacks 49
Converting from Infix to Postfix: Example (2)
Chapter 5: Stacks 50
Converting Infix to Postfix: convert
1. Set postfix to an empty StringBuilder
2. Set operator stack to an empty stack
3. while more tokens in infix string
4. Get the next token
5. if the token is an operand
6. Append the token to postfix
7. else if the token is an operator
8. Call processOperator to handle it
9. else
10. Indicate a syntax error
11. Pop remaining operators and add them to postfix
Chapter 5: Stacks 51
Converting Infix to Postfix: processOperator
Chapter 5: Stacks 52
Converting Infix to Postfix
Chapter 5: Stacks 53
Infix toPostfix: Code
import java.util.*;
public class InfixToPostfix {
private Stack<Character> opStack;
private static final String OPERATORS = “+-*/”;
private static final int[] PRECEDENCE =
{1, 1, 2, 2};
private StringBuilder postfix;
private boolean isOperator (char ch) {
return OPERATORS.indexOf(ch) >= 0;
}
private int precedence (Character ch) {
return (ch == null) ? 0 :
PRECEDENCE[OPERATORS.indexOf(ch)];
}
Chapter 5: Stacks 54
Infix toPostfix: Code (2)
public String convert (String infix)
throws SyntaxErrorException {
opStack = new Stack<Character>();
postfix = new StringBuilder();
StringTokenizer tokens =
new StringTokenizer(infix);
try {
while (tokens.hasMoreTokens()) { loop body }
... after loop ...
} catch (EmptyStackException exc) {
throw new SyntaxErrorException(...);
}
}
Chapter 5: Stacks 55
Infix toPostfix: Code (3)
// loop body
String token = tokens.nextToken();
Character first = token.charAt(0);
if (Character.isJavaIdentifierStart(first) ||
Character.isDigit(first)) {
postfix.append(token);
postfix.append(‘ ‘);
} else if (isOperator(first)) {
processOperator(first)
} else {
throw new SyntaxErrorException(...);
}
Chapter 5: Stacks 56
Infix toPostfix: Code (4)
// after loop
while (!opStack.empty()) {
char op = opStack.pop();
postfix.append(op);
postfix.append(‘ ‘);
}
return postfix.toString();
Chapter 5: Stacks 57
Infix toPostfix: Code (5)
private Character top () {
return opStack.empty() ? null : opStack.peek();
}
Chapter 5: Stacks 58
Infix toPostfix: Handling Parentheses
private static final String OPERATORS = “+-*/()”;
private static final int[] PRECEDENCE =
{2, 2, 3, 3, 1, 1};
Chapter 5: Stacks 59
Infix toPostfix: Handling Parentheses (2)
private void processOperator (Character op) {
if (op.charValue() != ‘(‘) {
while (precedence(op) <= precedence(top())) {
char top = opStack.pop();
if (top == ‘(‘) break;
postfix.append(top);
postfix.append(‘ ‘);
}
}
if (op.charValue() != ‘)’) {
opStack.push(op);
}
}
Chapter 5: Stacks 60
Infix toPostfix: Handling Parentheses (3)
// in convert, after token loop:
while (!opStack.empty()) {
char op = opStack.pop();
if (op == ‘(‘)
throw new SyntaxErrorException(...);
// because ( is unmatched
postfix.append(op);
postfix.append(‘ ‘);
}
Chapter 5: Stacks 61
Infix to Postfix: Making It Even Cleaner
• Can push a special “open bracket” first
• Stack is never empty
• This will not pop off: we give it very low precedence
• Push the same special bracket at the end:
• Will cause other operators to pop off
• Make bracket/parenthesis handling less special
• Add notion of left and right precedence
• Left value is for already scanned operator (on left)
• Right value is for not yet pushed operator (on right)
Chapter 5: Stacks 62
Infix to Postfix: Making It Even Cleaner (2)
Operator + * ( )
Left Precedence 3 4 1 ---
Right Precedence 3 4 5 2
Chapter 5: Stacks 63