π Linked Lists: The Train of Data
Imagine a train where each car knows only about the next car. Thatβs a linked list!
π What is a Linked List?
Picture a conga line at a party. Each person holds onto the shoulders of the person in front. Nobody can see the whole line, but everyone knows whoβs next!
Thatβs a Singly Linked List:
- Each person = a Node
- Holding shoulders = the Next pointer
- The first person = the Head
- The last person (holding nothing) = Tail (points to null)
class Node {
int data; // What you're holding
Node next; // Who's in front of you
Node(int data) {
this.data = data;
this.next = null;
}
}
graph LR HEAD["π§ Head"] --> A["π¦ 10"] A --> B["π¦ 20"] B --> C["π¦ 30"] C --> NULL["β null"]
Why Use Linked Lists?
| Arrays | Linked Lists |
|---|---|
| Fixed size | Grows freely |
| Fast access by index | No index needed |
| Slow insert/delete | Fast insert/delete |
π‘οΈ Sentinel and Dummy Node Pattern
The Problem
When adding or removing the first node, you need special code:
// Messy! Different logic for head
if (head == null) {
head = newNode;
} else {
// find position...
}
The Solution: Dummy Node
Put a fake guard at the front! This guard never leavesβit just points to the real first node.
// Create dummy (sentinel) node
Node dummy = new Node(-1);
dummy.next = head;
Think of it like a bouncer at a club. Heβs always there, but heβs not actually part of the party!
graph LR DUMMY["π‘οΈ Dummy"] --> A["π¦ 10"] A --> B["π¦ 20"] B --> NULL["β null"]
Magic of Dummy Nodes
Before (messy):
if (head.val == target) {
head = head.next;
} else {
Node prev = head;
// find and delete...
}
After (clean):
Node dummy = new Node(0);
dummy.next = head;
Node prev = dummy;
// Same logic works for ALL cases!
return dummy.next; // Real head
Rule: When in doubt, use a dummy node!
π’π Floydβs Cycle Detection
The Mystery
How do you know if a train track loops back on itself?
You canβt just walk foreverβyou might never stop!
The Clever Solution
Send two runners:
- π’ Tortoise: Moves 1 step at a time
- π Hare: Moves 2 steps at a time
If thereβs a loop, the fast runner will lap the slow one!
boolean hasCycle(Node head) {
Node slow = head;
Node fast = head;
while (fast != null &&
fast.next != null) {
slow = slow.next; // 1 step
fast = fast.next.next; // 2 steps
if (slow == fast) {
return true; // They met! Loop!
}
}
return false; // Fast hit the end
}
graph LR A["π¦ 1"] --> B["π¦ 2"] B --> C["π¦ 3"] C --> D["π¦ 4"] D --> B style D fill:#ff6b6b
Why Does This Work?
Imagine a circular track:
- If you run twice as fast as me
- Youβll catch up by 1 step each round
- Eventually, you MUST hit me!
Time: O(n) | Space: O(1)
π― Finding the Middle of a Linked List
The Challenge
Without knowing the length, how do you find the middle?
Same Trick: Fast & Slow Pointers!
When the fast pointer reaches the end, the slow pointer is at the middle!
Node findMiddle(Node head) {
Node slow = head;
Node fast = head;
while (fast != null &&
fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow; // Middle!
}
Visualization:
Start: [1] [2] [3] [4] [5]
S
F
Step 1: [1] [2] [3] [4] [5]
S
F
Step 2: [1] [2] [3] [4] [5]
S
F
Fast is done! Slow = Middle = 3
Even Length: Returns second middle node
[1] [2] [3] [4] β Returns 3
π Linked List Reversal
The Classic Problem
Turn 1 β 2 β 3 β null into 3 β 2 β 1 β null
Think of it Likeβ¦
People in a conga line turning around one by one:
- Each person lets go of the person in front
- Grabs the person behind
- Now facing the other way!
The Three-Pointer Dance
Node reverse(Node head) {
Node prev = null;
Node curr = head;
while (curr != null) {
Node next = curr.next; // Save
curr.next = prev; // Flip!
prev = curr; // Move
curr = next; // Move
}
return prev; // New head
}
Step-by-step:
Original: null β [1] β [2] β [3] β null
prev curr
Step 1: null β [1] [2] β [3] β null
prev curr
Step 2: null β [1] β [2] [3] β null
prev curr
Step 3: null β [1] β [2] β [3] null
prev curr
Done! Return prev = [3]
π Reverse Linked List II
The Twist
Reverse only part of the list!
Example:
Input: 1 β 2 β 3 β 4 β 5
Reverse from position 2 to 4
Output: 1 β 4 β 3 β 2 β 5
The Strategy
- Walk to position before the start
- Reverse the middle section
- Reconnect the ends
Node reverseBetween(Node head,
int left, int right) {
Node dummy = new Node(0);
dummy.next = head;
Node prev = dummy;
// Step 1: Walk to left-1
for (int i = 1; i < left; i++) {
prev = prev.next;
}
// Step 2: Reverse [left, right]
Node curr = prev.next;
for (int i = 0; i < right - left; i++) {
Node next = curr.next;
curr.next = next.next;
next.next = prev.next;
prev.next = next;
}
return dummy.next;
}
graph TD A["Before: 1β2β3β4β5"] --> B["Find start position"] B --> C["Reverse 2β3β4"] C --> D["After: 1β4β3β2β5"]
πͺ Reverse Nodes in k-Group
The Grand Challenge
Reverse nodes in groups of k!
Example (k=3):
Input: 1 β 2 β 3 β 4 β 5 β 6 β 7
Output: 3 β 2 β 1 β 6 β 5 β 4 β 7
\_______/ \_______/ β
Group 1 Group 2 Leftover
The Algorithm
- Count k nodes (do we have enough?)
- Reverse those k nodes
- Recursively handle the rest
- Connect everything
Node reverseKGroup(Node head, int k) {
// Count k nodes
Node curr = head;
int count = 0;
while (curr != null && count < k) {
curr = curr.next;
count++;
}
// Not enough? Keep as is
if (count < k) return head;
// Reverse k nodes
Node prev = null;
curr = head;
for (int i = 0; i < k; i++) {
Node next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
// head is now tail of reversed
// Connect to rest (recursively)
head.next = reverseKGroup(curr, k);
return prev; // New head
}
Visual Journey
k=2: [1]β[2]β[3]β[4]β[5]
Round 1: Reverse [1,2]
[2]β[1] [3]β[4]β[5]
Round 2: Reverse [3,4]
[2]β[1]β[4]β[3] [5]
Round 3: Only [5] left (< k)
[2]β[1]β[4]β[3]β[5]
π Summary: Your Linked List Toolkit
| Technique | When to Use | Key Idea |
|---|---|---|
| Singly Linked List | Dynamic data | Nodes + pointers |
| Dummy Node | Edge cases | Fake first node |
| Floydβs Cycle | Loop detection | Slow + Fast |
| Find Middle | Half the list | Same trick! |
| Reversal | Flip direction | 3 pointers |
| Reverse II | Partial flip | Walk + reverse |
| k-Group | Chunk reverse | Count + recurse |
π‘ Golden Rules
- When stuck: Draw it out! Boxes and arrows.
- Edge cases: Empty list? One node? Use dummy!
- Slow + Fast: Solves cycles AND middle problems
- Reversal: Always track
prev,curr,next - Partial reversal: Find the boundaries first
Youβve got this! π
Linked lists are like LEGO chainsβonce you understand how pieces connect, you can build anything!