Web Analytics

๐Ÿ”— Singly Linked List

Beginner to Intermediate ~15 min read

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

Output
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

  1. Structure: Chain of nodes, each with data + next pointer
  2. Strength: O(1) insert/delete at head, dynamic size
  3. 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!

Practice: Implement a linked list from scratch. Try reversing it. Practice on paper first - draw the pointers!