Lesson · 40 min · Free
Stacks & Queues in Python
Stacks & Queues in Python body { font-family: sans-serif; line-height: 1.6; margin: 20px; } h1, h2 { color: #2c3e50; } pre { background-color: #ecf0f1; padding: 15px; border-radius: 5px; overflow-x: auto; } code { font-f
Stacks & Queues in Python
Welcome to this lesson on Stacks and Queues in Python, crucial data structures with wide-ranging applications, including those relevant to computational biology and pharmacy. While Python lists offer a flexible way to store collections of items, understanding the specific behaviors of stacks and queues allows us to model real-world processes more accurately and efficiently. For instance, imagine managing a queue of samples waiting for analysis in a lab, or tracking the order of reactions in a biochemical pathway – these scenarios inherently follow stack or queue principles. At an abstract level, both stacks and queues are linear data structures, meaning elements are arranged sequentially. The key difference lies in how elements are added and removed. This behavior is often described by acronyms: LIFO (Last-In, First-Out) for stacks, and FIFO (First-In, First-Out) for queues. Python lists can be adapted to mimic both of these behaviors, making them excellent tools for implementing these concepts.
Understanding Stacks: LIFO Principle
A stack operates on the LIFO principle, much like a stack of plates. The last plate you put on top is the first one you'd remove. In programming terms, elements are "pushed" onto the top of the stack and "popped" off from the top. Common operations include push (add an element), pop (remove the top element), and peek (view the top element without removing it). Stacks are fundamental in many algorithms, such as function call management in interpreters, undo/redo functionalities, and parsing expressions. In a pharmaceutical context, consider a stack representing a history of drug interactions being evaluated. The most recent interaction added to the stack would be the first one you'd want to review or undo. Or, imagine a stack of tasks for a robotic arm in a high-throughput screening facility; the last task added might be the most urgent to complete. # Implementing a Stack using Python's list stack = [] # Push elements onto the stack stack.append("Sample_A_Batch_1") stack.append("Sample_B_Batch_1") stack.append("Sample_C_Batch_2") print(f"Current stack: {stack}") # Output: ['Sample_A_Batch_1', 'Sample_B_Batch_1', 'Sample_C_Batch_2'] # Pop an element from the stack (LIFO) removed_sample = stack.pop() print(f"Removed: {removed_sample}") # Output: Removed: Sample_C_Batch_2 print(f"Stack after pop: {stack}") # Output: ['Sample_A_Batch_1', 'Sample_B_Batch_1'] # Peek at the top element (without removing) if stack: # Check if stack is not empty top_sample = stack[-1] print(f"Top sample (peek): {top_sample}") # Output: Top sample (peek): Sample_B_Batch_1 # Check if stack is empty print(f"Is stack empty? {not bool(stack)}") # Output: Is stack empty? False
Understanding Queues: FIFO Principle
A queue, unlike a stack, follows the FIFO principle – First-In, First-Out. Think of a line at a pharmacy counter: the first person to arrive is the first person to be served. Elements are "enqueued" (added) at the rear (or back) of the queue and "dequeued" (removed) from the front. Queues are essential for managing resources, scheduling tasks, and handling sequential data processing. In a biotech setting, a queue could represent a sequence of patient samples awaiting a specific diagnostic test, where the oldest sample needs to be processed first to maintain sample integrity or meet turnaround times. Another example might be a queue of computational jobs submitted to a bioinformatics server, where jobs are processed in the order they were received. While Python lists can simulate queues using append() for enqueuing and pop(0) for dequeuing, pop(0) is inefficient for large lists because it requires shifting all subsequent elements. For more efficient queue implementations, especially in performance-critical applications, Python's collections.deque (double-ended queue) is preferred as it provides O(1) time complexity for adding and removing elements from both ends. import collections # Implementing a Queue using collections.deque queue = collections.deque() # Enqueue elements to the queue queue.append("Patient_ID_101") queue.append("Patient_ID_102") queue.append("Patient_ID_103") print(f"Current queue: {queue}") # Output: deque(['Patient_ID_101', 'Patient_ID_102', 'Patient_ID_103']) # Dequeue an element from the queue (FIFO) served_patient = queue.popleft() # Use popleft() for dequeues print(f"Served: {served_patient}") # Output: Served: Patient_ID_101 print(f"Queue after dequeue: {queue}") # Output: deque(['Patient_ID_102', 'Patient_ID_103']) # Peek at the front element (without removing) if queue: front_patient = queue[0] print(f"Next in queue (peek): {front_patient}") # Output: Next in queue (peek): Patient_ID_102 # Check if queue is empty print(f"Is queue empty? {not bool(queue)}") # Output: Is queue empty? False
Key Takeaways
Stacks are LIFO (Last-In, First-Out) data structures. Queues are FIFO (First-In, First-Out) data structures. Python lists can implement stacks using append() for push and pop() for pop. Python's collections.deque is the efficient way to implement queues, using append() for enqueue and popleft() for dequeue. Understanding these structures is crucial for managing sequential processes, resource allocation, and algorithm design in scientific computing.
Practice Exercise: Simulating a Reaction Buffer Preparation
You are tasked with simulating the preparation of a reaction buffer in a laboratory. The protocol states that components must be added in a specific order, but some components are volatile and must be used immediately after being prepared. Use a Python list to simulate a stack for managing the addition of buffer components. Your task is to: Initialize an empty list called reaction_buffer_components . Add the following components, one by one, to the buffer: "Water", "Tris-HCl", "NaCl", "EDTA", "Enzyme". After adding all components, simulate the "use" of the last two components added (e.g., "Enzyme" and then "EDTA"), as they are volatile. Print which component is being used each time. Finally, print the remaining components in the reaction_buffer_components list.
Watch the full lesson — free
This topic is part of Python Programming - Basics, a complete AI-narrated video course. Press play once and watch the entire lecture like a movie.
Start the course free →