๐ Singly Linked List
Arrays require contiguous memory and fixed size. What if you need dynamic growth? Enter Linked Lists - a chain of nodes connected by pointers. Each node holds data and a reference to the next node. Perfect for when you need O(1) insert/delete at the beginning!
The Chain of Nodes
What is a Linked List?
A linked list is a sequence of nodes where:
- Each node contains: Data + Pointer to next node
- Head: First node in the list
- Tail: Last node (points to null)
- Dynamic: Grows and shrinks as needed
head โ [10|โ] โ [20|โ] โ [30|โ] โ null
Node Structure
The Building Block
class Node:
def __init__(self, data):
self.data = data # Store value
self.next = None # Pointer to next node
That's it! Just data and a next pointer.
See It in Action
Watch how linked list operations work:
How Operations Work - Step by Step
Understanding linked list operations is crucial. Let's see exactly what happens during each operation, even if you don't play the animation!
1. Insert at HEAD - O(1)
Why O(1)? Only 3 steps!
Starting state:
HEAD โ [10] โ [20] โ [30] โ null
Want to insert 5 at HEAD:
Step 1: Create new node with value 5
[5] (new node, not connected yet)
Step 2: Point new node's next to current HEAD
[5] โ [10] โ [20] โ [30] โ null
โ
(points to old HEAD)
Step 3: Update HEAD to point to new node
HEAD โ [5] โ [10] โ [20] โ [30] โ null
โ New HEAD!
Result: Done in 3 operations - O(1)! No matter how long the list is, always 3 steps.
2. Insert at TAIL - O(n)
Why O(n)? Must traverse entire list!
Starting state:
HEAD โ [10] โ [20] โ [30] โ null
โ TAIL
Want to insert 40 at TAIL:
Step 1: Create new node with value 40
[40] (new node, not connected yet)
Step 2: Traverse from HEAD to find TAIL
Visit [10] โ not TAIL, continue
Visit [20] โ not TAIL, continue
Visit [30] โ next is null, this is TAIL!
Must visit every node - this is why it's O(n)!
Step 3: Point current TAIL's next to new node
HEAD โ [10] โ [20] โ [30] โ [40] โ null
โ old TAIL โ new TAIL
Result: If list has n nodes, must visit all n nodes - O(n)!
Optimization: Keep a tail pointer โ makes this O(1)!
3. Delete HEAD - O(1)
Why O(1)? Just move pointer!
Starting state:
HEAD โ [10] โ [20] โ [30] โ null
Want to delete HEAD (10):
Step 1: Identify current HEAD
HEAD โ [10] โ Delete this!
โ
[20] โ [30] โ null
Step 2: Move HEAD pointer to next node
[10] (will be garbage collected)
HEAD โ [20] โ [30] โ null
โ New HEAD!
Result: Done in 2 operations - O(1)! Old head is automatically garbage collected.
4. Search for Value - O(n)
Why O(n)? Must check each node!
Starting state:
HEAD โ [10] โ [20] โ [30] โ null
Want to find value 20:
Step 1: Start at HEAD, check value
HEAD โ [10] โ Check: 10 == 20? No, continue
โ
[20] โ [30] โ null
Step 2: Move to next, check value
HEAD โ [10] โ [20] โ Check: 20 == 20? YES! Found it!
โ
[30] โ null
Result: Worst case: value is at end or not found - must check all n nodes - O(n)!
Best case: Value is at HEAD - O(1)
Average case: Value is in middle - O(n/2) = O(n)
5. Delete TAIL - O(n)
Why O(n)? Must find second-to-last node!
Starting state:
HEAD โ [10] โ [20] โ [30] โ null
โ TAIL
Want to delete TAIL (30):
Problem: We need to set [20]'s next to null, but we can't go backwards!
Solution: Traverse to find second-to-last node
Visit [10] โ next.next exists, continue
Visit [20] โ next.next is null, this is second-to-last!
Step 2: Set second-to-last's next to null
HEAD โ [10] โ [20] โ null
โ new TAIL
[30] (will be garbage collected)
Result: Must traverse to find second-to-last - O(n)!
This is why doubly linked lists exist! They have prev pointers.
Key Insights
๐ก Understanding the Patterns
- O(1) operations: Work with HEAD directly (insert/delete at head)
- O(n) operations: Need to traverse the list (anything with TAIL, search)
- Why no backwards? Singly linked list only has "next" pointer, not "prev"
- Memory trade-off: Each node needs extra space for the "next" pointer
The Code
Click Run to execute your code
Quick Quiz
-
Why is insert at head O(1) but insert at tail O(n)?
Show answer
Insert at head: Just create new node, point it to current head, update head pointer - 3 operations, O(1)! Insert at tail: Must traverse entire list to find last node - O(n) operations. (Can be O(1) with tail pointer!) -
When would you use a linked list instead of an array?
Show answer
Use linked list when: (1) Frequent insert/delete at beginning, (2) Don't know size in advance, (3) Don't need random access. Use array when: (1) Need O(1) access by index, (2) Size is known, (3) Memory locality matters.
Complexity Analysis
| Operation | Time | Why |
|---|---|---|
| Insert at head | O(1) โ | Direct pointer update |
| Insert at tail | O(n) | Must traverse to end |
| Delete head | O(1) โ | Move head to next |
| Delete tail | O(n) | Must find second-last |
| Search | O(n) | Must check each node |
| Access by index | O(n) | Must traverse from head |
| Space | O(n) | Extra space for pointers |
Linked List vs Array
| Feature | Array | Linked List |
|---|---|---|
| Access by index | O(1) โ | O(n) |
| Insert at head | O(n) | O(1) โ |
| Insert at tail | O(1) โ | O(n) or O(1)* |
| Delete at head | O(n) | O(1) โ |
| Memory | Contiguous โ | Scattered |
| Size | Fixed | Dynamic โ |
| Cache locality | Better โ | Worse |
* O(1) with tail pointer
When to Use Linked Lists
โ Use Linked List When:
- Frequent insert/delete at beginning: O(1) operations!
- Unknown size: Dynamic growth
- Implementing stacks/queues: Perfect fit
- No random access needed: Sequential access is fine
โ Use Array Instead When:
- Need random access: Array is O(1), list is O(n)
- Cache locality matters: Arrays are contiguous
- Memory overhead matters: Pointers use extra space
- Size is known: Arrays are simpler
Common Mistakes
โ Losing References
# WRONG - loses rest of list!
head = head.next # Old head is lost!
# RIGHT - save reference first
temp = head
head = head.next
# Can still access temp if needed
โ Not Checking for NULL
# WRONG - crashes if list is empty!
print(head.data)
# RIGHT - check first
if head is not None:
print(head.data)
๐ก Key Takeaway
Singly Linked List is the foundation for dynamic data structures:
- โ O(1) insert/delete at head - faster than arrays!
- โ Dynamic size - grows as needed
- โ Foundation for stacks, queues, graphs
- โ ๏ธ O(n) access by index - slower than arrays
- โ ๏ธ Extra memory for pointers
Master pointers - they're everywhere in interviews!
Summary
The 3-Point Linked List Guide
- Structure: Chain of nodes, each with data + next pointer
- Strength: O(1) insert/delete at head, dynamic size
- Weakness: O(n) access by index, no cache locality
Interview tip: Always check for null pointers! Draw the list on paper to visualize pointer changes. Most linked list bugs are from losing references or null pointer errors.
What's Next?
You've learned the basics! Next: Linked List Reversal - one of the most common interview problems!
Enjoying these tutorials?