Pointer-based dynamic storage - Singly, Doubly, Circular
Data Structure Module - Learn how nodes connect through pointers
Think of a linked list like a treasure hunt:
Visual:
Array: [10] [20] [30] [40] ← All in a row, numbered boxes
Linked: [10|→] → [20|→] → [30|→] → [40|X]
Each box points to the next
X means "end" (null/None)
| Situation | Winner |
|---|---|
| Need random access (arr[5]) | Array (Best) |
| Frequent insertions at start | Linked List (Best) |
| Known size, rarely changes | Array (Best) |
| Size changes frequently | Linked List (Best) |
Each node has: data + next pointer
# Step 1: Create a Node class
class Node:
def __init__(self, data):
self.data = data # The value we store
self.next = None # Pointer to next node (starts as nothing)
# Step 2: Create the Linked List class
class LinkedList:
def __init__(self):
self.head = None # Points to first node
# Add to the END - O(n) because we traverse entire list
def append(self, data):
new_node = Node(data)
# If list is empty, new node becomes head
if not self.head:
self.head = new_node
return
# Otherwise, travel to the end
current = self.head
while current.next: # Keep going until no next
current = current.next
current.next = new_node # Link last node to new node
# Add to the BEGINNING - O(1) Super fast!
def prepend(self, data):
new_node = Node(data)
new_node.next = self.head # New node points to old head
self.head = new_node # New node becomes new head
# Print all values
def display(self):
elements = []
current = self.head
while current:
elements.append(str(current.data))
current = current.next
print(" → ".join(elements))
# Let's use it!
my_list = LinkedList()
my_list.append(10)
my_list.append(20)
my_list.append(30)
my_list.display() # Output: 10 → 20 → 30
my_list.prepend(5)
my_list.display() # Output: 5 → 10 → 20 → 30
Before: HEAD → [10] → [20] → [30] → X
Step 1: Create new node [5]
Step 2: Point [5] → HEAD (which is [10])
Step 3: Update HEAD → [5]
After: HEAD → [5] → [10] → [20] → [30] → X
Only 3 operations! Always O(1)!
def delete(self, data):
# Special case: deleting head
if self.head and self.head.data == data:
self.head = self.head.next # Skip the head
return
# Find the node BEFORE the one to delete
current = self.head
while current.next:
if current.next.data == data:
current.next = current.next.next # Skip over it!
return
current = current.next
Visual of Delete:
Before: [10] → [20] → [30] → X
Delete 20:
[10] → [30] → X (20 is skipped!)
Each node has: prev pointer + data + next pointer
Can traverse BOTH directions!
class DoublyNode:
def __init__(self, data):
self.data = data
self.prev = None # Points backward
self.next = None # Points forward
class DoublyLinkedList:
def __init__(self):
self.head = None
self.tail = None # Also track the end!
def append(self, data):
new_node = DoublyNode(data)
if not self.head:
self.head = self.tail = new_node
return
new_node.prev = self.tail # New node points back to old tail
self.tail.next = new_node # Old tail points forward to new
self.tail = new_node # Update tail
Advantages of Doubly Linked:
Disadvantage:
In a circular linked list, the last node points back to the first node instead of NULL.
Singly Linked List:
HEAD → [10] → [20] → [30] → NULL
Circular Linked List:
HEAD → [10] → [20] → [30] ┐
└─────────────────┘
Last node points back to HEAD!
Key Properties:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class CircularLinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
new_node.next = self.head # Points to itself!
return
# Traverse to last node
current = self.head
while current.next != self.head:
current = current.next
current.next = new_node
new_node.next = self.head # Complete the circle
def display(self):
if not self.head:
return
current = self.head
while True:
print(current.data, end=" → ")
current = current.next
if current == self.head: # Back to start
break
print("(back to head)")
cll = CircularLinkedList()
cll.append(10)
cll.append(20)
cll.append(30)
cll.display() # 10 → 20 → 30 → (back to head)
Combines doubly linked list (prev + next pointers) with circular structure.
┌───────────────────────────────┐
│ │
└─ [←|10|→] ↔ [←|20|→] ↔ [←|30|→] ┘
• Last node's NEXT → First node
• First node's PREV → Last node
• Traversal in BOTH directions, continuously
Properties:
| Type | Pointers per Node | NULL at End? | Traversal |
|---|---|---|---|
| Singly | 1 (next) | Yes | Forward only |
| Doubly | 2 (prev + next) | Yes | Both directions |
| Circular Singly | 1 (next) | No | Forward, looping |
| Circular Doubly | 2 (prev + next) | No | Both, looping |
| Operation | Array | Singly Linked | Doubly Linked |
|---|---|---|---|
| Access by index | O(1) (Best) | O(n) | O(n) |
| Insert at head | O(n) | O(1) (Best) | O(1) (Best) |
| Insert at tail | O(1) | O(n) | O(1) (Best) |
| Delete (known node) | O(n) | O(n)* | O(1) (Best) |
*Need to find previous node first
When to use Linked List:
One-liner for each concept:
| Concept | Key Takeaway |
|---|---|
| Singly Linked | Nodes point to next; O(1) insert at head, but O(n) access/search. |
| Doubly Linked | Nodes point to next AND prev; allows backward traversal. |
| Circular Linked | Last node points back to the head; useful for round-robin scheduling. |
| Head Pointer | The gateway to the list; lose this, and you lose the whole list! |
| Tail Pointer | Optional pointer to the last node; makes appending to the end O(1). |
Essential Code Snippets:
# Reversing a Singly Linked List (Classic Question)
def reverse_list(head):
prev, curr = None, head
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
return prev
# Finding Middle of a Linked List (Slow/Fast Pointer)
def find_middle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
The Golden Rules:
head is None), Single node list.curr.next before changing it.Video Courses:
Articles & Visualizations:
Practice Problems:
Test your understanding with step-by-step solutions
15 questions · 90s per question
Each question has a 90-second time limit. Unanswered questions will be auto-submitted when time runs out.