Use arrow keys or buttons to navigate • Interactive slides ahead!
1 / 21
What is a Linked List?
A linked list is a linear data structure where each element (called a node) contains data and a reference (pointer) to the next node.
Analogy: Treasure Hunt
Each clue tells you where to find the next clue. You can only follow the chain forward — you cannot jump directly to clue #5.
Analogy: Train Cars
Each car (node) carries cargo (data) and is coupled to the next car (next pointer). The engine is the head. The last car points to null.
Key Idea
Unlike arrays, linked list nodes do NOT need to be stored contiguously in memory. They can be scattered anywhere — the pointers connect them into a logical sequence.
2 / 21
Array vs. Linked List
Array: Contiguous Memory
Linked List: Scattered Memory
Operation
Array
Linked List
Winner
Access by index
O(1)
O(n)
Array
Insert at front
O(n)
O(1)
Linked List
Insert at end
O(1)*
O(n)
Array
Insert at middle
O(n)
O(n)
Tie
Delete at front
O(n)
O(1)
Linked List
Search
O(n)
O(n)
Tie
Memory overhead
Low
Higher (ptrs)
Array
* Amortized O(1) for dynamic arrays. Worst case O(n) when resizing.
3 / 21
The Node Class
Every linked list is built from Node objects. Each node stores data and a reference to the next node.
Java Implementation
public classNode {
int data; // the value storedNode next; // pointer to next nodepublicNode(int data) {
this.data = data;
this.next = null;
}
}
The next field is itself a Node reference — this creates the chain. When next is null, we've reached the end.
Anatomy of a Single Node
Key Idea
A Node is a self-referential structure: it contains a reference to another object of its own type. This is the fundamental building block for all linked structures.
4 / 21
Singly Linked List Structure
A singly linked list has: a head pointer, one-way traversal, and a null terminator.
Java Wrapper Class
public classSinglyLinkedList {
privateNode head; // entry pointprivate int size; // # of elementspublicSinglyLinkedList() {
this.head = null;
this.size = 0;
}
}
Three Things to Remember
head — the only entry point into the list. Lose it and you lose everything.
Singly linked — each node only knows about the next node.
null terminator — the last node's next is null.
5 / 21
Creating a Linked List (Step by Step)
Build the list 10 → 20 → 30 from scratch. Click through each step.
Step 0 / 3 — Click Next
// Click "Next Step" to begin building the list
Warning: Never Lose the Head!
If you accidentally reassign head without saving the old reference, all nodes become unreachable.
6 / 21
Traversal
Walking through every node using a temporary current pointer.
Java Code
public voidprintList() {Node current = head;while (current != null) { System.out.print(current.data + " → "); current = current.next; } System.out.println("null");}
Key Idea
Use a temporary pointer so we don't modify head.
Step-by-Step
Click "Start Traversal" to watch current walk through the list.
7 / 21
Insert at Head — O(1)
The fastest insertion: add a new node at the front.
Insert value 5 at head — Click Next
public voidinsertAtHead(int data) {Node newNode = newNode(data); newNode.next = head; // link first! head = newNode; // then move head size++;}
Order Matters!
WRONG:head = newNode first → self-loop, list lost!
RIGHT: Link first, then move head.
8 / 21
Insert at Tail — O(n)
Must traverse the entire list to find the last node before appending.
Insert 40 at tail — Click Next
public voidinsertAtTail(int data) {
Node newNode = newNode(data);
if (head == null) {
head = newNode;
} else {
Node current = head;
while (current.next != null)
current = current.next;
current.next = newNode;
}
size++;
}
Why O(n)?
No direct reference to the tail, so we walk all n nodes. A tail pointer makes this O(1).
9 / 21
Insert at Position — O(n)
Insert at index k by finding the node at k-1 and rewiring pointers.
Insert 25 at index 2 — Click Next
public voidinsertAt(int index, int data) {
if (index == 0) { insertAtHead(data); return; }
Node newNode = newNode(data);
Node prev = head;
for (int i = 0; i < index - 1; i++)
prev = prev.next;
newNode.next = prev.next; // link first!
prev.next = newNode; // then rewire
size++;
}
Order of Assignments
WRONG:prev.next = newNode first — breaks the chain!
RIGHT: Set newNode.next FIRST, THEN prev.next.
10 / 21
Delete from Head — O(1)
Remove the first node by advancing the head pointer.
Delete head node — Click Next
public intdeleteFromHead() {
if (head == null)
throw newNoSuchElementException();
int data = head.data;
head = head.next;
size--;
return data;
}
Memory Management
Java / Python: Garbage collector frees the orphaned node.
C / C++: You must manually free() the removed node.
11 / 21
Delete from Tail — O(n)
Find the second-to-last node, set its next to null.
Delete tail node — Click Next
public intdeleteFromTail() {
if (head == null) throw newNoSuchElementException();
if (head.next == null) {
int data = head.data; head = null; size--; return data;
}
Node current = head;
while (current.next.next != null)
current = current.next;
int data = current.next.data;
current.next = null;
size--;
return data;
}
Why O(n)?
No backwards pointer. Must traverse from head to second-to-last. A doubly linked list solves this.
12 / 21
Delete by Value — O(n)
Find the node containing value 20 and remove it using two pointers.
Delete value 20 — Click Next
public booleandeleteByValue(int target) {
if (head == null) return false;
if (head.data == target) {
head = head.next; size--; return true;
}
Node prev = head;
Node current = head.next;
while (current != null) {
if (current.data == target) {
prev.next = current.next;
size--; return true;
}
prev = current;
current = current.next;
}
return false;
}
Two-Pointer Technique
We track prev and current because removing a node requires updating the previous node's next. We can't go backwards in a singly linked list.
13 / 21
Search / Contains — O(n)
Linear search to find a value. Try it below!
Java Code
public booleancontains(int target) {
Node current = head;
while (current != null) {
if (current.data == target)
return true;
current = current.next;
}
return false;
}
No Random Access
Unlike arrays (arr[i] in O(1)), linked lists require walking from the head. Searching is always O(n) worst case.
Interactive Search
Enter a value and click Search to animate.
14 / 21
Get Size / Length
Two approaches: traverse and count, or maintain a size variable.
Approach 1: Count by Traversal — O(n)
public intgetSize() {
int count = 0;
Node current = head;
while (current != null) {
count++;
current = current.next;
}
return count;
}
Approach 2: Maintain Size Field — O(1)
public classSinglyLinkedList {
privateNode head;
private int size; // track it!public intgetSize() {
return size; // O(1)!
}
public voidinsertAtHead(int data) {
// ... insertion logic ...
size++; // increment on insert
}
}
Best Practice
Always maintain a size field. Increment on insert, decrement on delete → O(1) size queries.
15 / 21
Sentinel / Dummy Head Node
A dummy node at the front eliminates special cases for empty list or head insertion/deletion.
Without Dummy
Must check if (head == null) everywhere. Special case for head operations!
With Dummy
Head is never null. Same logic for all positions. Simpler code!
// With dummy: insert at front — no special case!public voidinsertAtFront(int data) {
Node newNode = newNode(data);
newNode.next = head.next;
head.next = newNode;
size++;
}
Why Use a Sentinel?
head is never null
Front insert/delete = same logic as any position
Trade-off: one extra node of memory
16 / 21
Common Pitfalls
Bugs that trip up nearly every student implementing a linked list for the first time.
1. NullPointerException
Calling current.next when current is null. Always check first!
2. Losing References
3. Off-by-One Errors
Traversing to index k? Stop at k-1 for insertion. < vs <= matters!
4. Forgetting to Update Size
Every insert: size++. Every delete: size--. One miss corrupts everything.
5. Not Handling Edge Cases
Empty list (head == null)
Single-element list
Deleting the head node
17 / 21
Time Complexity Summary
Operation
Singly Linked List
Array (fixed)
ArrayList (dynamic)
Insert at head
O(1)
O(n)
O(n)
Insert at tail
O(n)*
O(1)†
O(1) amortized
Insert at index k
O(k)
O(n)
O(n)
Delete at head
O(1)
O(n)
O(n)
Delete at tail
O(n)
O(1)
O(1)
Delete by value
O(n)
O(n)
O(n)
Access by index
O(n)
O(1)
O(1)
Search
O(n)
O(n)
O(n)
Get size
O(1)‡
O(1)
O(1)
* O(1) with tail pointer † Only if space available ‡ If you maintain a size field
When to Choose a Linked List
Use when you need frequent insertions/deletions at the head, don't need random access, and size is unpredictable. Use an array for fast random access with stable size.
18 / 21
Real-World Applications
Implementing Stacks & Queues
Music Playlist
Undo Functionality
Also Used In...
Operating system process scheduling
Hash table chaining (collision resolution)
Graph adjacency lists
Memory allocation (free lists)
19 / 21
Summary & Cheat Sheet
INSERT AT HEAD: O(1)
newNode.next = head;
head = newNode;
INSERT AT TAIL: O(n)
traverse to last node
last.next = newNode;
DELETE FROM HEAD: O(1)
head = head.next;
SEARCH: O(n)
traverse, compare each
TRAVERSAL PATTERN:
Node cur = head;
while (cur != null) {
// process cur.data
cur = cur.next;
}
Golden Rules
Never lose the head — always use a temporary pointer.
Link before you redirect — set newNode.next before head.
Check for null — before accessing .data or .next.
Handle edge cases — empty list, single element, head deletion.
Maintain size — increment/decrement on every insert/delete.
Train Cars Recap
Engine = head Car = node Coupling = next pointer
Last car → null Insert = couple in Delete = uncouple
Coming Next
Doubly Linked Lists — adding a prev pointer for backward traversal and O(1) tail ops.
20 / 21
Challenge: Fix the Bug!
This insertAtHead has a critical bug. Can you spot it?
Buggy Code
public voidinsertAtHead(int data) {Node newNode = newNode(data); head = newNode; // line 3 newNode.next = head; // line 4 size++;}
Click the line(s) you think are wrong.
Which line has the bug?
What happens with this code?
The Fix
Swap lines 3 and 4! You must link newNode.next = head BEFORE overwriting head. Otherwise head already points to newNode, so line 4 creates a self-loop and the original list is lost forever.
newNode.next = head; // ✓ link first! head = newNode; // ✓ then move head
Challenge: Predict the State
Starting with list [5, 10, 15, 20, 25], what does the list look like after these operations?