Skip to content

Stacks ​

Chapter 17

Objectives ​

In this lecture, you will:

  • Learn about stacks.

  • Examine various stack operations.

  • Learn how to implement a stack as an array.

  • Learn how to implement a stack as a linked list.

  • Learn how to use a stack to solve problems like removing recursion.

  • Understand common stack operations and their complexity.

  • Learn typical error conditions and how to handle them.

Introduction ​

Stack: a data structure in which elements are added and removed from one end only.

  • Addition and deletion occurs only at the top of the stack.

  • Last in, first out (LIFO) data structure

  • Operations:

    • Push: to add an element onto the stack

    • Pop: to remove an element from the stack

    • Peek or Top: inspect the top element without removing it

    • isEmpty: test whether the stack contains no elements

    • isFull: (for fixed-capacity stacks) test whether the stack is at capacity

Examples of Real-Life Stacks
CoinsTraysBoxesBooks
stack of coinsstack of traysstack of boxesstack of books

Stack Example ​

See the lecture video above for the full example.

Stack Interface ​

UML class diagram for  interfaceUML class diagram for  interface

Array Stack ​

Array-Based Stack Implementation ​

  • The first element goes in the first array position, the second in the second position, etc.

  • The top of the stack is an index of the last element added to the stack.

  • Stack elements may be stored in an array, which is a random-access data structure.

    • The stack element is accessed only through the top.
  • To track the top position, use a variable.

  • Can dynamically allocate an array.

  • Enables the user to specify the size of the array.

  • The size attribute will hold the number of elements currently in the array.

  • The top index is typically size - 1 when size counts elements.

  • The capacity attribute will hold the maximum number of elements of the array before it needs to be resized.

1 / 1
Example of layout and operations of an array-based stack.

Resizing considerations

  • If the array is fixed-size, pushing when size == capacity causes overflow.
  • A common dynamic strategy is to allocate a new array with larger capacity (for example double the previous capacity) and copy elements. This gives amortized time for push.
    • Amortized time differs from probabilistic average time.
    • It is the average cost per operation over a sequence of operations.
  • Shrinking on pop is possible but should be done carefully to avoid frequent reallocations.

Empty Array-Based Stack ​

See the lecture video above for the full example.

Linked Stack ​

Linked Implementation of a Stack ​

  • Array only allows a fixed number of elements.

  • If the number of elements to be pushed exceeds the array size, the program may terminate.

  • Linked lists can dynamically organize data.

  • In a linked representation, mpHead is a pointer to the memory address of the top element in the stack.
    (mpHead may be named mpStackTop.)

Linked-list considerations

  • push and pop are operations because they only change a set number of pointers.

  • Linked stacks do not require contiguous memory but have per-node overhead (pointer and allocation cost).

See the lecture video above for the full example.

Push ​

See the lecture video above for the full example.

Pop ​

See the lecture video above for the full example.

Postfix Notation ​

Infix Notation ​

Infix notation: usual notation for writing arithmetic expressions

  • Operator is written between the operands.

  • Example: .

  • Evaluates from left to right.

  • Operators have precedence.

  • Parentheses can be used to override precedence.

Prefix Notation (Polish Notation) ​

Prefix (Polish) notation: operators are written before the operands

  • Introduced by the Polish mathematician Jan Lukasiewicz in the early 1920s.

  • Parentheses can be omitted.

  • Example: becomes .

Postfix Notation (Reverse Polish Notation) ​

Postfix notation: operators follow the operands (postfix operators)

  • Proposed by Australian philosopher and early computer scientist, Charles L. Hamblin, in the late 1950s.

  • Advantage: operators appear in the order required for computation.

Advantages and tradeoffs

  • No need for parentheses when operator precedence is encoded in the order.

  • Easy to evaluate using a stack because operands are available when an operator is encountered.

  • Example: becomes

  • Example: becomes

Postfix Notation Examples ​

Infix ExpressionEquivalent Postfix Express

Postfix Expressions Calculator ​

Postfix notation has important applications in computer science.

Many compilers first translate arithmetic expressions into postfix notation and then translate this expression into machine code.

Evaluation algorithm:

  • Scan expression from left to right.

  • When an operator is found, back up to get operands, perform the operation, and continue.

Expression:

See the lecture video above for the full example.

Postfix Processing Algorithm (for binary operators):

  1. Initialize an empty stack.
  2. For each token in the postfix expression:
    • If the token is an operand, push it onto the stack.
    • If the token is a binary operator, pop the top two operands (right then left), apply the operator, and push the result.
    • If a pop is attempted when the stack has fewer than the required operands, signal an error (underflow / malformed expression).
  3. When the input ends, the stack should contain exactly one element: the result. If there are more elements, the expression was malformed.

Symbols can be numbers or anything else:

  • , , , and are operators, require two operands.

    • Pop stack twice and evaluate the expression.

    • If the stack has less than two elements, then error.

  • If the symbol is , the expression ends.

    • Pop and print the answer from the stack.

    • If the stack has more than one element, then error.

  • If the symbol is anything undefined, the expression contains an illegal operator.

Start to the function:

cpp
bool evaluatePostfixExpression(ifstream &input, double& result)
{
  Stack<double> stack;
  char nextSymbol;
  double value;

  // ...
  nextSymbol = input.peek();
  // ...
}

Notes on error handling

  • Decide whether the API throws exceptions on underflow/overflow or returns error codes. Be consistent across the codebase.
  • For numeric evaluation, consider handling divide-by-zero and invalid tokens.

Iterating Over a Linked List in Reverse ​

  • To print a list backward non-recursively, first get to the last node of the list.

    • Problem: Links go in only one direction.

    • Solution: Save a pointer to each node in a stack.
      Uses the LIFO principle.

  • Since the number of nodes is usually not known, use the linked implementation of a stack.

Alternative approaches ​

  • Use recursion to visit nodes in reverse; this uses the call stack and may risk stack overflow on very deep lists.

  • If frequent reverse iteration is required, consider a doubly-linked list.

See the lecture video above for the full example.

Summary ​

Stack items are added/deleted from one end.

  • Last-In, First-Out (LIFO) data structure

  • Operations: push, pop, peek/top, isEmpty, isFull (for fixed size)

  • Can be implemented as an array or linked list.

  • Middle elements should not be accessed directly.

  • Time complexity: push, pop, and peek are time (if not resizing an array).

  • Space and performance: array stacks use contiguous memory and have lower per-element overhead; linked stacks use extra memory per node but grow without large reallocations.

  • Postfix notation: operators are written after the operands(no parentheses needed); this form is convenient for stack-based evaluation and for compiler internals.