Hello! Welcome back to your course on Data Structures and Algos.
In our last lesson, you saw how hash maps serve as a powerful tool for frequency counting, allowing us to solve anagram and other character-based problems efficiently. We focused on using a data structure to solve a problem. Today, we'll shift our focus to the construction of data structures themselves.
This lesson covers two of the most fundamental linear data structures: stacks and queues. Your learning outcome is to implement stacks and queues using both arrays and linked lists. Understanding these implementations from scratch is crucial for technical interviews, as it demonstrates a solid grasp of data structure mechanics and their performance trade-offs.
Let's begin by defining these structures.
1. Stacks and Queues: The Core Principles
At a high level, both stacks and queues are collections of items. What distinguishes them is the rule governing how items are added and removed.
- A Stack follows the LIFO (Last-In, First-Out) principle. The last item you add is the first one you get to take out. Think of a stack of plates: you add a new plate to the top and you also remove a plate from the top.
- A Queue follows the FIFO (First-In, First-Out) principle. The first item you add is the first one you get to take out. This is exactly like a line at a grocery store: the first person in line is the first person to be served.
To get a quick overview of these concepts and their standard operations, you can watch the introductory part of the following video.
L1. Introduction to Stack and Queue | Implementation using Data Structures
This video from take U forward provides a clear animated explanation of the LIFO and FIFO principles, and introduces the core operations for stacks and queues.
Watch from 00:42 to 08:43. Focus on differentiating the behavior of pop in a stack versus a queue.
2. Implementing a Stack
A stack's primary operations are:
push(item): Add an item to the top.pop(): Remove and return the item from the top.top()orpeek(): Return the top item without removing it.isEmpty(): Check if the stack is empty.size(): Return the number of items.
We can build a stack using either an array (or a dynamic list) or a linked list.

2.1. Stack Implementation using an Array (or Python List)
Using a dynamic array, like a Python list, is the most straightforward method. The "top" of the stack corresponds to the end of the list.
push(item)is implemented withlist.append(item).pop()is implemented withlist.pop().top()is implemented by accessing the last element,list[-1].
These operations are all amortized O(1), making this a very efficient approach.
Python Stack Data Structure: When and Why to Use it
The Codecademy article 'Python Stack Data Structure' provides a clear, practical guide on implementing stacks in Python. We'll focus on the implementation sections.
Please read the section 'Different ways to implement a stack in Python'. Pay attention to the code examples for implementing a stack with a list and a linked list. Note the advantages and disadvantages mentioned for each.
While using Python's built-in list is simple, interviewers might ask you to implement it from scratch using a fixed-size array to test your understanding of the underlying mechanics. This involves manually managing an index or pointer for the top of the stack.
For a deeper look into this lower-level implementation, you can review the array-based stack section from the "take U forward" video, which demonstrates managing the top index explicitly. This is not something you'd typically code in Python but is excellent foundational knowledge.
2.2. Stack Implementation using a Linked List
A singly linked list is also a natural fit for a stack. The "top" of the stack is simply the head of the list.
push(item): Create a new node and make it the newhead, pointing itsnextto the oldhead.pop(): Store thehead's value, update theheadtohead.next, and return the stored value.top(): Return the value of theheadnode.
All these operations are a true O(1) time complexity because they only involve manipulating the head pointer.
The Codecademy article you just read provided a Python class for this. For another excellent walkthrough, the following video shows a step-by-step Python implementation.
Implementing a Stack and a Queue with a Singly Linked List in Python 3 (Tutorial)
This 'Live Python' video demonstrates how to build a stack from scratch in Python using a singly linked list.
Watch from 01:52 to 06:49. Focus on how the push and pop methods manipulate the head of the linked list to achieve LIFO behavior.
Test your understanding!
Imagine you have an empty stack. The following operations are performed in order: push(10), push(20), pop(), push(30), push(40), pop(). What is the value of the element at the top of the stack?
Show answer
push(10): Stack is[10]push(20): Stack is[10, 20]pop(): Returns20. Stack is[10]push(30): Stack is[10, 30]push(40): Stack is[10, 30, 40]pop(): Returns40. Stack is[10, 30]
The element at the top of the stack is 30.
3. Implementing a Queue
A queue's primary operations are:
enqueue(item): Add an item to the back (rear) of the queue.dequeue(): Remove and return the item from the front of the queue.front()orpeek(): Return the front item without removing it.isEmpty(): Check if the queue is empty.size(): Return the number of items.
3.1. Queue Implementation using an Array (or Python List)
A naive implementation using a Python list might look like this:
enqueue(item)useslist.append(item). This is O(1).dequeue()useslist.pop(0). This is O(n) because all subsequent elements must be shifted one position to the left.
This inefficiency for dequeue is a critical point to raise in an interview. While simple, it's not performant for large queues. The proper array-based solution is a circular queue, which uses two pointers (front and rear) and allows the queue to wrap around the end of the array.
L1. Introduction to Stack and Queue | Implementation using Data Structures
To understand how to efficiently implement a queue with an array, we need to look at the circular array concept. The 'take U forward' video provides an excellent, detailed explanation of this logic.
Watch from 18:50 to 28:00. Focus on how the start and end pointers move and how the modulo operator (%) is used to make the queue 'wrap around' the array. This is a common advanced implementation detail asked in interviews.
In Python, the collections.deque object is a highly optimized double-ended queue (which can act as a regular queue) that provides O(1) appends and pops from both ends. It's the standard library's solution to this problem.
3.2. Queue Implementation using a Linked List
A linked list provides a very efficient way to build a queue. To achieve O(1) for both enqueue and dequeue, we need to maintain pointers to both the front (head) and rear (tail) of the list.
enqueue(item): Add a new node at therearof the list and update therearpointer.dequeue(): Remove the node from thefrontof the list and update thefrontpointer.

The W3Schools article on queues provides a complete Python class for a linked-list-based queue.
Read the section 'Queue Implementation using Linked Lists'. Study the Node and Queue classes. Pay attention to how the front and rear pointers are managed, especially in the enqueue and dequeue methods and the edge case where the queue becomes empty.
4. Summary of Trade-offs
Choosing the right underlying structure is a matter of trade-offs. Here’s a summary that is crucial for interview discussions:
| Data Structure | Implementation | Push / Enqueue | Pop / Dequeue | Key Considerations |
|---|---|---|---|---|
| Stack | Python List (Dynamic Array) | O(1) Amortized | O(1) Amortized | Simple, idiomatic in Python. Amortized cost due to potential resizing. |
| Stack | Linked List | O(1) | O(1) | True O(1) performance. Slightly more memory overhead per element (for pointers). |
| Queue | Python List (Naive) | O(1) | O(n) | Simple to write but inefficient. Avoid in production/interviews for dequeue. |
| Queue | Circular Array | O(1) | O(1) | Efficient but more complex to implement (managing pointers and size). Fixed capacity. |
| Queue | Linked List (with head/tail) | O(1) | O(1) | Efficient and dynamic. The classic and robust implementation. |
Conclusion
In this lesson, you've gone deep into the mechanics of stacks and queues, learning how to build them from the ground up using both arrays and linked lists. This foundational knowledge is non-negotiable for anyone serious about acing technical interviews.
Key Takeaways:
- Stack (LIFO): Easily implemented with a dynamic array's
append/popor a linked list'sadd_to_head/remove_from_head. Both are efficient. - Queue (FIFO): A naive implementation with a list's
pop(0)is inefficient (O(n)). The correct implementations use a circular array or a linked list withfrontandrearpointers to achieve O(1) enqueue and dequeue operations. - Understand the "Why": Be ready to explain why
list.pop(0)is slow and how a linked list or circular array solves this problem. This demonstrates a deeper level of understanding.
Now that you know how to build and analyze these structures, our next lesson will focus on applying them. You will apply stacks to solve classic problems like parentheses matching and expression evaluation, which are common interview questions that directly test your understanding of the LIFO principle.