Stack

A stack is a fundamental linear data structure that follows the Last-In-First-Out (LIFO) principle. Think of it like a stack of plates - you can only add or remove plates from the top, and the last plate you put on is the first one you'll take off.

Interactive Stack Visualization

Experience the LIFO principle in action! Watch how elements are pushed onto and popped off the stack, and see how the top indicator always points to the most recently added element.

Stack Operations (LIFO - Last In, First Out)

Push (Add to Top)

Add element to top of stack

Pop (Remove from Top)

Remove element from top

Top value: 30

Peek (View Top)

Highlight top element

Stack Visualization

TOP
stack[0]
10
stack[1]
20
stack[2]
30

Stack Height: 3 / 8

Status: 3 elements in stack

Stack Contents (bottom to top): [10, 20, 30]

How Stack Operations Work:

Push (O(1)):

Add a new element to the top of the stack. The last element added becomes the new top.

Pop (O(1)):

Remove and return the top element from the stack. Follows Last-In-First-Out (LIFO) principle.

Peek (O(1)):

View the top element without removing it. Useful for checking what's on top without modifying the stack.

LIFO Principle:

Stacks follow the Last-In-First-Out principle, meaning the last element added (pushed) is the first one to be removed (popped). Think of a stack of plates - you add and remove plates from the top, not the bottom.

Understanding LIFO (Last-In-First-Out)

The Plate Stack Analogy

Imagine a stack of dinner plates in your kitchen:

  • Adding plates: You place new plates on top of the stack
  • Taking plates: You remove plates from the top, one at a time
  • Last plate in: The most recently added plate
  • First plate out: That same recent plate is removed first
  • Bottom plates: Can't be accessed until top plates are removed

LIFO in Programming

This principle applies to many programming scenarios:

  • Function calls: Most recent call executes first
  • Undo operations: Last action undone first
  • Expression evaluation: Nested operations resolved inside-out
  • Browser history: Back button follows LIFO order
  • Memory management: Local variables in nested scopes

Common Use Cases

Function Call Stack

When your program calls functions, each call is pushed onto a call stack:

main() → calls funcA() → calls funcB()
Stack: [main, funcA, funcB] ← top

Functions complete in reverse order: funcB finishes first, then funcA, then main.

Undo/Redo Functionality

Text editors and applications use stacks to track user actions:

Actions: Type "Hello" → Delete "o" → Type "p"
Undo Stack: [Type "Hello", Delete "o", Type "p"] ← top

Pressing Undo reverses the most recent action first.

Complexity Analysis

Operation Time Complexity Space Complexity Explanation
Push O(1) O(1) Add element to top in constant time
Pop O(1) O(1) Remove top element in constant time
Peek O(1) O(1) Access top element without removal

Why O(1)? All stack operations only interact with the top element, regardless of how many elements are in the stack. No iteration or searching required!

Stack Class Implementation

Complete stack implementations with push, pop, and peek methods. Each example includes the plate stack analogy in comments and demonstrates both basic operations and practical applications.

class Stack:
    """
    A Stack implementation using Python list
    Demonstrates LIFO (Last-In-First-Out) principle
    """
    
    def __init__(self):
        """Initialize an empty stack"""
        self.items = []
    
    def push(self, item):
        """
        Add an item to the top of the stack
        Like placing a plate on top of a stack of plates
        
        Time Complexity: O(1)
        Space Complexity: O(1)
        """
        self.items.append(item)
        print(f"Pushed {item} onto stack")
    
    def pop(self):
        """
        Remove and return the top item from the stack
        Like taking the top plate from a stack of plates
        
        Time Complexity: O(1)
        Space Complexity: O(1)
        
        Raises:
            IndexError: If stack is empty
        """
        if self.is_empty():
            raise IndexError("Cannot pop from empty stack")
        
        item = self.items.pop()
        print(f"Popped {item} from stack")
        return item
    
    def peek(self):
        """
        Return the top item without removing it
        Like looking at the top plate without taking it
        
        Time Complexity: O(1)
        Space Complexity: O(1)
        
        Raises:
            IndexError: If stack is empty
        """
        if self.is_empty():
            raise IndexError("Cannot peek at empty stack")
        
        return self.items[-1]
    
    def is_empty(self):
        """Check if the stack is empty"""
        return len(self.items) == 0
    
    def size(self):
        """Return the number of items in the stack"""
        return len(self.items)
    
    def display(self):
        """Display stack contents from top to bottom"""
        if self.is_empty():
            return "Empty Stack"
        
        print("Stack (top to bottom):")
        for i in range(len(self.items) - 1, -1, -1):
            print(f"  [{i}] {self.items[i]}")

# Example usage demonstrating LIFO principle
def demonstrate_lifo():
    """Demonstrate how stack follows LIFO principle"""
    stack = Stack()
    
    print("=== Demonstrating LIFO Principle ===")
    print("Think of this like stacking plates...")
    
    # Push items (like stacking plates)
    print("\nStacking plates:")
    stack.push("Plate 1 (first)")
    stack.push("Plate 2 (second)")  
    stack.push("Plate 3 (third)")
    
    print(f"\nTop plate is: {stack.peek()}")
    print(f"Stack size: {stack.size()}")
    
    # Pop items (like taking plates off)
    print("\nTaking plates off (LIFO order):")
    while not stack.is_empty():
        plate = stack.pop()
        print(f"Took: {plate}")
    
    print(f"\nStack is now empty: {stack.is_empty()}")

# Function call stack simulation
def recursive_function_demo(n):
    """Demonstrate how function calls use a stack"""
    print(f"Calling function with n = {n}")
    
    if n <= 1:
        print(f"Base case reached with n = {n}")
        return 1
    else:
        result = n * recursive_function_demo(n - 1)
        print(f"Returning {result} for n = {n}")
        return result

if __name__ == "__main__":
    # Basic stack operations
    demonstrate_lifo()
    
    print("\n" + "="*50 + "\n")
    
    # Function call stack example
    print("=== Function Call Stack Example ===")
    print("Computing factorial of 4:")
    factorial_4 = recursive_function_demo(4)
    print(f"\nFinal result: {factorial_4}")

Key Takeaways

Stack Advantages

  • Simple & Efficient: All operations are O(1)
  • Memory Efficient: No complex pointer management
  • Natural LIFO: Perfect for recursive and nested scenarios
  • Easy to Implement: Can use arrays or linked lists
  • Safe Access: Only top element is accessible

When to Use Stacks

  • Function Call Management: Automatic by programming languages
  • Expression Parsing: Mathematical expressions, parentheses matching
  • Undo Operations: Text editors, drawing applications
  • Backtracking Algorithms: Maze solving, depth-first search
  • Memory Management: Local variable scoping