Python Computer Science
Beginner
80 mins
Teacher/Student led
What you need:
Chromebook/Laptop/PC

Stacks, Queues, and Linked Lists

In this lesson, you'll explore essential data structures in computer science. Learn the concepts, operations, and real-world applications of these structures, then implement them in Python using VS Code. By the end, you'll create a simple project to apply your skills.
Learning Goals Learning Outcomes Teacher Notes

Live Class Feed

This is a live feed of the latest activity by your students on this lesson. It will update in real-time as they work on the lesson.
Load previous activity

    1 - Introduction

    In this lesson, you'll explore stacks, queues, and linked lists – fundamental data structures in computer science. You'll learn their concepts, operations, and real-world use cases, then implement them in Python using VS Code. By the end, you'll build a simple undo system project using a stack.

    By the end of this lesson, you will be able to:

    • Explain stacks, queues, and linked lists, including their operations.
    • Implement these data structures in Python.
    • Apply them to solve problems, like an undo feature.
    You'll need VS Code or a similar IDE with the Python extension.

    2 - Understanding Stacks

    A stack is a linear data structure that follows the Last In, First Out (LIFO) principle. Imagine a stack of plates: you add (push) to the top and remove (pop) from the top. This means the most recently added item is the first one to be removed, making it ideal for situations where you need to access the latest elements first.

    Key operations:

    • Push: Add an item to the top.
    • Pop: Remove and return the top item.
    • Peek: View the top item without removing it.
    • Is Empty: Check if the stack is empty.

    Use cases: Undo/redo in editors (last action undone first), function call stacks in programming, or browser history (back button pops the last page).

    Stacks can be implemented using lists in Python, with O(1) time for push and pop, meaning these operations are very fast regardless of the stack's size.

    Stacks are efficient for scenarios where you need to reverse operations or track history in LIFO order.

    3 - Implementing a Stack

    Now that you understand the concept of a stack, let's implement one in Python. A Stack class is like a blueprint or template that defines how a stack should work. It encapsulates the data (like the list of items) and the methods (functions) that operate on that data, such as pushing and popping items. This allows you to create multiple stack objects, each managing its own set of items independently. We'll create a Stack class using a Python list as the underlying storage, which is efficient for this purpose because appending and popping from the end of a list are fast operations.

    First, create a new folder called 'DataStructures' and inside it, create a file named 'my_stack.py'.

    class Stack:
        def __init__(self):
            # Initializes the stack with an empty list to store items
            self.items = []
    
        def push(self, item):
            # Adds an item to the top of the stack
            self.items.append(item)
    
        def pop(self):
            # Removes and returns the top item if the stack is not empty
            if not self.is_empty():
                return self.items.pop()
            return None
    
        def peek(self):
            # Returns the top item without removing it, if the stack is not empty
            if not self.is_empty():
                return self.items[-1]
            return None
    
        def is_empty(self):
            # Checks if the stack is empty
            return len(self.items) == 0
    
        def size(self):
            # Returns the number of items in the stack
            return len(self.items)

    Add the following complete code to define the Stack class. Here's a quick overview: the __init__ method initialises an empty list; push adds an item to the top using append; pop removes and returns the top item; peek lets you see the top item without removing it; is_empty checks if the stack has no items; and size returns the number of items.

    To test your Stack class and ensure all methods work correctly, add the following code at the end of the file. This creates a stack, checks if it's empty and its size, pushes two numbers, checks size and peeks, pops one (which should be the last one added, following LIFO), peeks at the remaining top item, checks size again, and finally checks if it's empty.

    if __name__ == "__main__":
        s = Stack()
        print(s.is_empty())  # Should print True
        print(s.size())      # Should print 0
        s.push(1)
        s.push(2)
        print(s.size())      # Should print 2
        print(s.peek())      # Should print 2
        print(s.pop())       # Should print 2
        print(s.peek())      # Should print 1
        print(s.size())      # Should print 1
        print(s.is_empty())  # Should print False
    Run your code. You should see the output: True, 0, 2, 2, 2, 1, 1, False. This confirms your stack is working correctly with all methods!

    4 - Stack Use Case: Undo System

    Stacks are perfect for undo systems because they follow the Last In, First Out (LIFO) principle. This means the most recent action or change is the first one you can undo, just like in a real text editor where pressing 'undo' reverses your last edit. In this step, we'll build a simple simulation of a text editor's undo feature. We'll create a class that tracks the history of text changes using a stack, allowing us to add new text (like typing) and then undo those changes step by step. This will help you see how stacks can be applied in real-world scenarios.

    Create a new file named 'undo_system.py'. This file will contain the UndoSystem class, which uses the Stack class from 'structures.py'. Add the following complete code to 'undo_system.py' (make sure to comment out any previous if __name__ == "__main__" block in 'my_stack.py' if it's interfering):

    from my_stack import Stack
    
    class UndoSystem:
        def __init__(self):
            self.history = Stack()  # Creates a stack to store previous text states
            self.current_text = ""  # Starts with empty text
    
        def edit(self, new_text):
            self.history.push(self.current_text)  # Pushes the current text to the stack before changing it
            self.current_text += new_text  # Appends the new text to the current text
            print(f"Current text: {self.current_text}")  # Prints the updated text
    
        def undo(self):
            if not self.history.is_empty():  # Checks if there's something to undo
                self.current_text = self.history.pop()  # Pops the last saved text and sets it as current
                print(f"Undo: Current text: {self.current_text}")  # Prints the restored text
            else:
                print("Nothing to undo")  # Message if the stack is empty
    
    if __name__ == "__main__":
        editor = UndoSystem()  # Creates an instance of UndoSystem
        editor.edit("Hello")  # Adds "Hello", prints: Current text: Hello
        editor.edit(", World")  # Adds ", World", prints: Current text: Hello, World
        editor.undo()  # Undoes the last edit, reverts to "Hello", prints: Undo: Current text: Hello
        editor.edit(" Everyone!")  # Adds " Everyone!", prints: Current text: Hello Everyone!
        editor.undo()  # Undoes again, reverts to "Hello", prints: Undo: Current text: Hello 
        editor.undo()  # Undoes again, reverts to "", prints: Undo: Current text:  

    Let's break down what the code does.

    • We import the Stack class from structures.py using from my_stack import Stack so that we can use the Stack class in this file.
    • The UndoSystem class uses your Stack class to store previous versions of the text.
    • The __init__ method initialises the stack and an empty current_text.
    • The edit method saves the current text to the stack (like taking a snapshot before a change) and then appends the new text, printing the updated text.
    • The undo method pops the last saved version from the stack and restores it as the current text, printing the result. If there's nothing to undo, it prints a message.
    • Finally, the test code creates an editor, performs some edits and undos to demonstrate the functionality.
    Run the file. Observe how each undo pops the last saved state from the stack, demonstrating LIFO in action. This shows how the stack keeps track of history efficiently. Think about how this could be expanded for more features, like redo.

    5 - Understanding Queues

    A queue is a linear data structure that follows the First In, First Out (FIFO) principle. Think of a queue at a shop: the first person in line is served first. In programming, this means the item that has been in the queue the longest is the one that gets processed first, ensuring fairness and order. This is different from a stack, where the newest item is processed first.

    Key operations:

    • Enqueue: Add an item to the back.
    • Dequeue: Remove and return the front item.
    • Peek: View the front item without removing it.
    • Is Empty: Check if the queue is empty.

    Use cases: Task scheduling (e.g., print jobs), breadth-first search in graphs, or handling requests in servers (first come, first served).

    Queues can be implemented using lists or collections.deque for efficiency (O(1) for enqueue and dequeue).

    Queues ensure fair processing in order of arrival.

    Unlock the Full Learning Experience

    Get ready to embark on an incredible learning journey! Get access to this lesson and hundreds more in our Digital Skills Curriculum.

    Copyright Notice
    This lesson is copyright of DigitalSkills.org. Unauthorised use, copying or distribution is not allowed.
    🍪 Our website uses cookies to make your browsing experience better. By using our website you agree to our use of cookies. Learn more