Stack ADT

Stack

Stacks:

Specification

stackpush(newEntry : T) : voidpop(): Tpeek() : TisEmpty() : booleanclear() : void
NameDescription
pushAdds a new entry to the top of the stack
popRemoves and returns the stack’s top entry
peekRetrieves the stack’s top entry without changing the stack
isEmptyReturns whether the stack is empty
clearRemoves all entries from the stack

Remember: You can only interact with the item at the top of the stack!

Example: Stack interface

public interface StackInterface<T> {
  void push(T newEntry);
  T pop();
  T peek();
  boolean isEmpty();
  void clear();
}

Design Decisions

Case: Stack is empty, what to do with pop and peak?

  1. Assume it isn’t empty
  2. Return null
  3. Throw an exception.

Security Note:

Using Stack on Algebraic Expressions

Relevancy: “The most important ADT in computer science”

Infix, Prefix, Postfix

Algebraic Notation:

  1. Infix: Each binary operator appears between its operations
  2. Prefix: Each binary operator appears before its operands
  3. Postfix: Each binary operator appears after is operands

Note: Why Prefix and Postfix Matter

Example: The same expression in infix, prefix, and postfix

# Infix
a + b
# Prefix
+ a b
# Postfix
a b +

Example: The same expression in infix, prefix, and postfix

# Infix
a + b / c 
# Prefix
+ a / b c
# Postfix
a b c / +

Warning: Keep terms on their original sides when converting expressions

Testing Balanced Expressions

Balanced Expression: An expression with matching brackets and parenthesis.

Q: How do you tell if an infix expression is balanced?

A: Read the expression character-by-character, and:

  1. Push each opening symbol (e.g., [, {) to the stack as you read it
  2. Pop each closing symbol (e.g., ], }) from the stack as you read it
  3. When/if you reach the end of the expression:

Converting Infix \to Postfix

Q: How do you programmatically convert an infix expression \to postfix?

A: Read the expression character-by-character and use a stack to store operators:

  1. Variables (e.g., a, b) get appended to the postfix expression as we read them.
  2. Operators (e.g., -, +) get pushed into the operator stack as we read them.

Remember: The parenthesis themselves don’t get added into postfix notation.

Example: Converting infix \to postfix with a stack

  1. Infix Expression: a+b*c
Current CharacterPostfix FormOperator Stack
aa
+a+
bab+
*ab+*
cabc+*
abc*+
abc*+
  1. Infix Expression: a - b + c
Current CharacterPostfix FormOperator Stack
aa
-a-
bab-
+ab-+
cab-c+
ab-c+
  1. Infix Expression: a ^ b ^ c
Current CharacterPostfix FormOperator Stack
aa
^a^
bab^
^ab^^
cabc^^
abc ^^
abc ^^
  1. Infix Expression: a / b * ( c + (d-e))
Current CharacterPostfix FormOperator Stack
aa
/a/
bab/
*ab/*
(ab/*(
cab/c*(
+ab/c*(+
(ab/c*(+(
dab/cd*(+(
-ab/cd*(+(-
eab/cde*(+(-
)ab/cde-*(+(
)ab/cde-+*(
)ab/cde-+*

Note: When implementing this in code, make sure that the expression is balanced, or throw an exception when something illegal happens.

Evaluating Postfix Expressions

Q: How do you programmatically evaluate a postfix expression?

A: We’ll use a stack to evaluate it.

  1. Create a stack (s)
  2. For every token (t),
    1. If t is a variable:
      • Push t to stack. (s.push(t))
    2. Else:
      • t is an operator (op = t)
      • Pop the right-hand-side variable (rhs = s.pop())
      • Pop the left-hand-side variables (lhs = s.pop())
      • Push the result back to the stack (s.push(lhs op rhs))
      • (If any of the pops returned incorrect values, the expression was malformed)
  3. Return s.pop()

The Application Program Stack

Stacks make method calling and recursion possible.

Example: Program activation records

public static void main(string[] args) {
  int x = 5;
  int y = methodA(x);
}
public static int methodA(int a) {
  a = methodB(a);
  return a;
}
public static int methodB(int b) {
  return b;
}

So, by the time methodB() begins execution, the program activation record looks like this:

  1. methodB
  2. methodA
  3. main

Java Class Library

Found in java.util

Methods:

T push(T item);
T pop();
T peek();
boolean empty();

Implementing a Stack

Linked Implementation

Implementing a stack with a singly linked list is trivial.

Example: Implementing basic stack methods with SLI

public final class LinkedStack<T> implements StackInterface {
  private Node head;
?   /* constructor goes here */
  public T peek() {
      if (isEmpty()) {
          throw new EmptyStackException();
      }
      return head.data;
  }
  public T pop() {
      T top = peek();
      head = head.next;
      return top;
  }
  public T push(T entry) {
      Node newEntry = Node(entry);
      if (head != null) {
          newEntry.next = head;
      }
      head = newEntry;
  }
  /* private Node inner-class here */
}

Array-Based Implementation

Implementing a stack with an array is best done by using the end of the array as the top of the stack, as it’s the easiest to access.

Vector-Based Stack Implementation

Vector: Object that behaves like a high-level array.