Category 11

Linked Lists

Master linked list manipulation techniques including reversing, merging, cycle detection, and pointer arithmetic. These fundamental patterns appear in countless interview problems and real-world systems.

10
Problems
3
Difficulty Levels
100%
Complete Solutions
6
LeetCode Problems
Theory

Linked List Fundamentals & Patterns

Linked lists are foundational data structures requiring careful pointer manipulation. Master the core patterns and you'll handle even the trickiest list problems with confidence.

1. ListNode Class & Basic Structure

All linked list problems in C# work with the standard ListNode definition:

csharp
public class ListNode {
    public int val;
    public ListNode next;
    public ListNode(int val = 0, ListNode next = null) {
        this.val = val;
        this.next = next;
    }
}

2. Singly vs Doubly Linked Lists

Singly: Each node has a next pointer only. Efficient forward traversal, harder backward.

Doubly: Each node has next and prev pointers. Bidirectional traversal, but more memory.

Most interview problems use singly linked lists unless explicitly stated.

3. Dummy Head Node Technique

A dummy node pointing to the head simplifies edge cases (removing first node, merging lists):

csharp
// Create dummy node to unify edge cases
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode current = dummy;

// Now you can safely modify head without special cases
while (current.next != null) {
    // Process and potentially modify head
    current = current.next;
}

return dummy.next; // Always return through dummy

4. Fast & Slow Pointer (Floyd's Cycle Detection)

Two pointers moving at different speeds detect cycles and find middle nodes:

csharp
// Detect cycle: slow moves 1 step, fast moves 2 steps
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
    slow = slow.next;
    fast = fast.next.next;
    if (slow == fast) {
        // Cycle detected
        return true;
    }
}
return false;

5. Reversing a Linked List

Iterative approach (3-pointer): Maintain prev, current, next to reverse in-place.

csharp
public ListNode ReverseList(ListNode head) {
    ListNode prev = null;
    ListNode current = head;

    while (current != null) {
        ListNode nextTemp = current.next;  // Save next
        current.next = prev;                      // Reverse link
        prev = current;                            // Move prev forward
        current = nextTemp;                        // Move current forward
    }

    return prev; // New head
}

6. Two Pointer Pattern: Tracking Gaps

To find and manipulate nodes N steps from end, use two pointers with a fixed gap:

csharp
// Set up pointers N steps apart
ListNode first = head, second = head;
for (int i = 0; i < N; i++) {
    if (first == null) return null;
    first = first.next;
}

// Move both until first reaches end
while (first != null) {
    first = first.next;
    second = second.next;
}

// Now second is at desired position
💡
Memory vs Clarity: Singly linked lists use less memory than doubly-linked, but require careful pointer manipulation. Always draw diagrams for pointer operations to avoid null reference exceptions.
Problems
1. Reverse Linked List
Given the head of a singly linked list, reverse it and return the reversed list. LeetCode 206.
Easy LC-206 Iterative
1

Problem Statement

Reverse a singly linked list. You may do it iteratively or recursively. The iterative approach is preferred for interviews.

Example

Input
1 → 2 → 3 → null
Output
3 → 2 → 1 → null

Approach

1
Initialize three pointers: prev (null), current (head), nextTemp
2
Save the next node before reversing the link
3
Reverse current's next pointer to point to prev
4
Move prev and current one step forward, repeat until null
csharp
public class Solution {
    public ListNode ReverseList(ListNode head) {
        ListNode prev = null;
        ListNode current = head;

        while (current != null) {
            // 1. Save next node
            ListNode nextTemp = current.next;

            // 2. Reverse the link
            current.next = prev;

            // 3. Move pointers forward
            prev = current;
            current = nextTemp;
        }

        // prev is now the new head
        return prev;
    }
}

Trace: Input [1, 2, 3]

Step prev current nextTemp Action
0 null 1 — Initialize
1 1 2 2 1.next = null
2 2 3 3 2.next = 1
3 3 null null 3.next = 2, return 3

Complexity & Edge Cases

Time
O(n)
Space
O(1)
⚠️
Edge case: Empty list or single node. Both handled correctly—loop doesn't execute or executes once.
2. Merge Two Sorted Lists
Merge two sorted linked lists into one sorted list. LeetCode 21.
Easy LC-21 Dummy Head
2

Problem Statement

You are given the heads of two sorted linked lists list1 and list2. Merge them into one sorted list.

Example

Input
list1: 1→2→4, list2: 1→3→4
Output
1→1→2→3→4→4

Approach

1
Create a dummy node to simplify edge cases (no special handling for head)
2
Compare values from both lists and append the smaller node
3
Advance the pointer from the list whose node was appended
4
Append any remaining nodes from the non-empty list
csharp
public class Solution {
    public ListNode MergeTwoLists(ListNode list1, ListNode list2) {
        // Dummy node eliminates edge case for head assignment
        ListNode dummy = new ListNode(0);
        ListNode current = dummy;

        // Compare and merge while both lists have nodes
        while (list1 != null && list2 != null) {
            if (list1.val <= list2.val) {
                current.next = list1;
                list1 = list1.next;
            } else {
                current.next = list2;
                list2 = list2.next;
            }
            current = current.next;
        }

        // Append remaining nodes (one list will be null)
        if (list1 != null) {
            current.next = list1;
        } else {
            current.next = list2;
        }

        return dummy.next;
    }
}

Trace: Input [1→2→4], [1→3→4]

Step list1 list2 Comparison Appended
1 1 1 equal list1: 1
2 2 1 list2 < list1 list2: 1
3 2 3 list1 < list2 list1: 2
4 4 3 list2 < list1 list2: 3
5 4 4 equal list1: 4
6 null 4 — Append list2: 4

Complexity & Edge Cases

Time
O(n + m)
Space
O(1)
⚠️
Edge cases: One or both lists empty. Handled by final if-else—just append remaining non-null list.
3. Linked List Cycle
Detect if a singly linked list contains a cycle. LeetCode 141.
Easy LC-141 Floyd's Algorithm
3

Problem Statement

Given the head of a linked list, determine if the list has a cycle in it. A cycle exists if a node's next pointer eventually points back to a node already visited.

Example

Input
3→2→0→-4 (tail points to node 1)
Output
true

Approach: Floyd's Cycle Detection

1
Initialize slow pointer (1 step) and fast pointer (2 steps)
2
If no cycle, fast reaches end (null)
3
If cycle, fast and slow eventually meet
4
Return true if pointers meet, false if fast reaches null
csharp
public class Solution {
    public bool HasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }

        ListNode slow = head;
        ListNode fast = head;

        // Move slow 1 step, fast 2 steps
        while (fast != null && fast.next != null) {
            slow = slow.next;           // 1 step
            fast = fast.next.next;      // 2 steps

            // If they meet, there's a cycle
            if (slow == fast) {
                return true;
            }
        }

        // If we reach here, no cycle
        return false;
    }
}

Why It Works: The Math

If a cycle exists with length C and the distance from head to cycle start is D:

  • When slow enters cycle, fast is already D steps into it
  • Fast gains 1 position on slow per iteration (2 steps vs 1 step)
  • Eventually they must collide (slow vs fast closing the gap in cycle)

Complexity & Edge Cases

Time
O(n)
Space
O(1)
💡
Better than HashSet: O(n) time but O(1) space vs O(n) space with HashSet. Preferred for interviews.
4. Linked List Cycle II
Find the node where the cycle begins. LeetCode 142.
Medium LC-142 Math + Pointers
4

Problem Statement

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Example

Input
3→2→0→-4 (cycle at node 2)
Output
Node with value 2

Approach: Floyd + Two Pointers

1
Use Floyd to detect if cycle exists (slow and fast meet)
2
Move one pointer to head, keep other at meeting point
3
Move both one step at a time—they meet at cycle start
4
Return the meeting point
csharp
public class Solution {
    public ListNode DetectCycleStart(ListNode head) {
        if (head == null || head.next == null) {
            return null;
        }

        ListNode slow = head;
        ListNode fast = head;

        // Phase 1: Detect cycle
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;

            if (slow == fast) {
                // Cycle detected
                break;
            }
        }

        // No cycle
        if (fast == null || fast.next == null) {
            return null;
        }

        // Phase 2: Find cycle start
        // Move one pointer to head
        slow = head;

        // Move both one step at a time
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }

        // They meet at cycle start
        return slow;
    }
}

Mathematical Proof

Let D = distance from head to cycle start, C = cycle length, when they meet at point x (x steps into cycle):

  • Slow traveled: D + x steps
  • Fast traveled: D + x + k·C steps (completed k cycles)
  • Fast = 2 × Slow: D + x + k·C = 2(D + x), solve: D = k·C - x
  • Result: Moving from head while other stays at x will meet at cycle start

Complexity & Edge Cases

Time
O(n)
Space
O(1)
⚠️
Critical: The proof depends on slow entering the cycle before their first meeting. Fast always catches slow due to speed ratio.
5. Remove Nth Node From End
Remove the Nth node from the end of the list. LeetCode 19.
Medium LC-19 Two Pointers
5

Problem Statement

Given the head of a linked list, remove the Nth node from the end of the list and return the head.

Example

Input
1→2→3→4→5, n=2
Output
1→2→3→5

Approach: Two Pointers with N-Step Gap

1
Use dummy node to handle removal of head
2
Create first pointer, advance it N steps
3
Create second pointer at dummy, move both until first reaches end
4
Second pointer is now N-1 before target, skip the Nth node
csharp
public class Solution {
    public ListNode RemoveNthFromEnd(ListNode head, int n) {
        // Dummy helps when removing head
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        ListNode first = dummy;
        ListNode second = dummy;

        // Move first pointer n+1 steps ahead
        for (int i = 0; i <= n; i++) {
            if (first == null) {
                return head; // n is larger than list
            }
            first = first.next;
        }

        // Move both pointers until first reaches end
        while (first != null) {
            first = first.next;
            second = second.next;
        }

        // Remove the nth node
        second.next = second.next.next;

        return dummy.next;
    }
}

Trace: Input [1→2→3→4→5], n=2

Phase Action first second
Setup i=0 dummy → 1 dummy
i=1 1 → 2 dummy
i=2 2 → 3 dummy
i=3 null dummy
Move Both Iteration 1 null 1
Iteration 2 null 2
Iteration 3 null 3
Remove 3.next = 5 — —

Complexity & Edge Cases

Time
O(n)
Space
O(1)
⚠️
Edge case: Removing the head (n = list length). Dummy pointer handles this elegantly.
6. Middle of Linked List
Find the middle node of a singly linked list. LeetCode 876.
Easy LC-876 Fast/Slow
6

Problem Statement

Given the head of a singly linked list, return the middle node. If there are two middle nodes, return the second one.

Example

Input
[1, 2, 3, 4, 5]
Output
Node 3

Approach: Fast & Slow Pointer

1
Initialize slow at head, fast at head
2
Move slow 1 step, fast 2 steps per iteration
3
When fast reaches end, slow is at middle
4
For even-length lists, returns second middle node
csharp
public class Solution {
    public ListNode MiddleNode(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;

        // Fast moves 2 steps, slow moves 1 step
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        // When fast reaches end, slow is at middle
        return slow;
    }
}

Trace: Input [1, 2, 3, 4, 5]

Iteration slow fast fast.next
0 1 1 2
1 2 3 4
2 3 5 null
3 3 (return) null —

Even-Length Behavior

For [1, 2, 3, 4]: Returns node 3 (second middle), not node 2. This is because fast.next becomes null before fast itself.

Complexity & Edge Cases

Time
O(n)
Space
O(1)
💡
Foundation problem: This pattern is used in many advanced problems like palindrome lists and quicksort on lists.
7. Palindrome Linked List
Determine if a linked list is a palindrome. LeetCode 234.
Medium LC-234 Reverse + Compare
7

Problem Statement

Given the head of a singly linked list, return true if it is a palindrome, or false otherwise.

Example

Input
[1, 2, 2, 1]
Output
true

Approach: Find Middle, Reverse Second Half, Compare

1
Find the middle node using fast/slow pointer
2
Reverse the second half of the list
3
Compare first half with reversed second half
4
Return true if all values match
csharp
public class Solution {
    public bool IsPalindrome(ListNode head) {
        // Step 1: Find middle
        ListNode slow = head;
        ListNode fast = head;

        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }

        // Step 2: Reverse second half
        ListNode reversedHalf = Reverse(slow);

        // Step 3: Compare first half and reversed second half
        ListNode left = head;
        ListNode right = reversedHalf;

        while (right != null) {
            if (left.val != right.val) {
                return false;
            }
            left = left.next;
            right = right.next;
        }

        return true;
    }

    private ListNode Reverse(ListNode head) {
        ListNode prev = null;
        ListNode current = head;

        while (current != null) {
            ListNode next = current.next;
            current.next = prev;
            prev = current;
            current = next;
        }

        return prev;
    }
}

Trace: Input [1, 2, 2, 1]

Step Description Result
1 Find middle with fast/slow slow at node 2 (second)
2 Reverse from middle 1←2←2→1, second half: 1→2
3 Compare: 1==1, 2==2 true

Complexity & Edge Cases

Time
O(n)
Space
O(1)
ℹ️
Note: This solution modifies the list during comparison. If preserving the list is required, restore it after checking.
8. Intersection of Two Lists
Find the intersection node of two singly linked lists. LeetCode 160.
Medium LC-160 Pointer Swap
8

Problem Statement

Given the heads of two singly linked lists headA and headB, return the node at which the two lists intersect. If lists don't intersect, return null.

Example

Input
A: 4→1→8→4→5, B: 5→6→1→8→4→5 (intersect at 8)
Output
Node with value 8

Approach: Pointer Swap Trick

1
Create two pointers, one at each head
2
When a pointer reaches null, redirect it to the other list's head
3
Both pointers traverse equal total distance if intersection exists
4
They meet at intersection or both reach null simultaneously
csharp
public class Solution {
    public ListNode GetIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }

        ListNode pA = headA;
        ListNode pB = headB;

        // Move both pointers, swap to other list when reaching end
        while (pA != pB) {
            // Move to next or redirect to other list's head
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }

        return pA; // Meeting point (or both null if no intersection)
    }
}

Why It Works

If lists have lengths m and n with intersection distance k from ends:

  • pA travels: m + n distance total before meeting
  • pB travels: n + m distance total before meeting
  • Both reach intersection at same time (equalized distances)
  • If no intersection, both reach null simultaneously and return null

Trace: A = [4→1→8→4→5], B = [5→6→1→8→4→5]

Iteration pA pB Action
0 4 5 Both advance
1 1 6 Both advance
2 8 1 Both advance
3 4 8 Both advance
4 5 4 Both advance
5 null→5 5 pA swaps to B
6 8 (meet) null→4 pB swaps to A

Complexity & Edge Cases

Time
O(n + m)
Space
O(1)
💡
Elegant solution: Better than length calculation approach. The swap automatically handles different lengths.
9. Add Two Numbers
Add two numbers represented as linked lists. LeetCode 2.
Medium LC-2 Simulation
9

Problem Statement

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each node contains a single digit. Add the two numbers and return the sum as a linked list.

Example

Input
l1 = [2→4→3], l2 = [5→6→4]
Output
[7→0→8] (342 + 465 = 807)

Approach: Digit-by-Digit Addition with Carry

1
Create dummy node to simplify list building
2
Iterate through both lists simultaneously
3
Add corresponding digits plus carry
4
Append new node with digit, update carry, continue
csharp
public class Solution {
    public ListNode AddTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode current = dummy;
        int carry = 0;

        // Process both lists and carry
        while (l1 != null || l2 != null || carry != 0) {
            // Get current digits or 0 if list is exhausted
            int val1 = l1 != null ? l1.val : 0;
            int val2 = l2 != null ? l2.val : 0;

            // Sum and separate digit from carry
            int sum = val1 + val2 + carry;
            int digit = sum % 10;
            carry = sum / 10;

            // Append new node to result
            current.next = new ListNode(digit);
            current = current.next;

            // Move to next nodes
            l1 = l1 != null ? l1.next : null;
            l2 = l2 != null ? l2.next : null;
        }

        return dummy.next;
    }
}

Trace: [2→4→3] + [5→6→4]

Iter l1 l2 sum digit carry
1 2 5 7 7 0
2 4 6 10 0 1
3 3 4 8 8 0

Complexity & Edge Cases

Time
O(max(m, n))
Space
O(max(m, n))
⚠️
Carry edge case: If final carry is non-zero (e.g., 999 + 1), create an extra node. Condition carry != 0 handles this.
10. Merge K Sorted Lists
Merge K sorted linked lists into one sorted list. LeetCode 23.
Hard LC-23 PriorityQueue
10

Problem Statement

You are given an array of K linked lists lists, each linked list is sorted in ascending order. Merge all the linked lists into one sorted linked list and return it.

Example

Input
[[1→4→5], [1→3→4], [2→6]]
Output
[1→1→2→3→4→4→5→6]

Approach: PriorityQueue (Min-Heap)

1
Create min-heap with custom comparator (by node value)
2
Add the head of each list to the heap
3
While heap not empty: pop min node, append to result, add its next
4
Return merged list from dummy node
csharp
public class Solution {
    public ListNode MergeKLists(ListNode[] lists) {
        // Min-heap using PriorityQueue (C# 10+)
        // Comparer: node with smaller value has higher priority
        var pq = new PriorityQueue<ListNode, int>();

        // Add all heads to heap
        foreach (var list in lists) {
            if (list != null) {
                pq.Enqueue(list, list.val);
            }
        }

        ListNode dummy = new ListNode(0);
        ListNode current = dummy;

        // Merge by always taking minimum
        while (pq.Count > 0) {
            ListNode minNode = pq.Dequeue();
            current.next = minNode;
            current = current.next;

            // Add next node from same list to heap
            if (minNode.next != null) {
                pq.Enqueue(minNode.next, minNode.next.val);
            }
        }

        return dummy.next;
    }
}

Trace: [[1→4→5], [1→3→4], [2→6]]

Step Heap Contents Dequeue Enqueue
Init [1, 1, 2] — —
1 [1, 2] 1 (list 1) 4
2 [2, 4] 1 (list 2) 3
3 [2, 3, 4] 2 (list 3) 6
4 [3, 4, 6] 3 4
5 [4, 4, 6] 4 5
6 [4, 5, 6] 4 —
7 [5, 6] 5 —
8 [6] 6 —

Alternative: Divide & Conquer

Recursively merge pairs of lists, reducing K lists to 1 in log(K) rounds. Time: O(N log K) vs PriorityQueue O(N log K) but better constant factors for small K.

Complexity Analysis

Time
O(N log K)
Space
O(K)

Where N = total nodes across all lists, K = number of lists

💡
Interview tip: Both PriorityQueue and divide-and-conquer are viable. Explain trade-offs and implement the one you're most confident with.