Algorithm

Intersection of Two Linked Lists

Two Pointer Pattern

Intersection of Two Linked Lists

Return the node at which two singly linked lists intersect. If they do not intersect, return null.

CONSTRAINTS
  • M, N up to 10,000
  • Original list structure must be preserved.
  • O(1) extra space required.
EXAMPLE 1
Input: listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], intersect at value 8
Output: Reference to the shared node 8
Both lists run down the same tail starting at that node; it is the first node they share by identity.
EXAMPLE 2
Input: listA = [2,6,4], listB = [1,5], no shared nodes
Output: null
The lists end in separate nodes and never merge, so there is no intersection.
Is the match by value or by the actual node?
By the actual node (identity). Two separate nodes with the same value are not an intersection.
What is returned when the lists never meet?
Null. With the two-pointer switch, both pointers reach the end together and the loop stops with them equal to null.
Can the lists have different lengths?
Yes — different prefix lengths are exactly the difficulty, and the switch (or the length-difference method) cancels that gap out.
Am I allowed to modify the lists?
No. The structure must be left intact, so approaches that relink nodes are off the table.

Finding the intersection of two linked lists is fundamentally about reconciling the different prefix lengths of the two paths. Our goal is to reach the junction point simultaneously, regardless of how much "extra" list precedes that junction in either list.

The Exhaustive Search (O(N * M))

The most straightforward approach is to iterate through every node in the first list and, for each node, scan through the entire second list to see if we find a reference match. This is highly inefficient because it performs a nested comparison for every node, resulting in quadratic time complexity.

python
# Brute force: nested comparison
def brute_force(headA, headB):
    currA = headA
    while currA:
        currB = headB
        while currB:
            if currA == currB: return currA
            currB = currB.next
        currA = currA.next
    return None
Intermediate: Hash Set (O(N + M) Time, O(N) Space)

By storing all nodes of List A in a set, we can iterate through List B and check if any node exists in the set. The first match is our intersection.

python
# Hash Set approach
def get_intersection(headA, headB):
    nodes_seen = set()
    while headA:
        nodes_seen.add(headA)
        headA = headA.next
    while headB:
        if headB in nodes_seen: return headB
        headB = headB.next
    return None
Intermediate: Length-Difference Approach (O(N + M) Time, O(1) Space)

If we know the lengths of both lists, we can ignore the extra prefix of the longer list. We calculate the difference d in lengths and advance the pointer of the longer list by d steps. Now, both pointers are equidistant from the junction and will meet after a simultaneous traversal.

python
# Length-difference approach
def get_intersection(headA, headB):
    lenA, lenB = get_len(headA), get_len(headB)
    while lenA > lenB: headA = headA.next; lenA -= 1
    while lenB > lenA: headB = headB.next; lenB -= 1
    while headA != headB:
        headA = headA.next; headB = headB.next
    return headA
Optimal Strategy: Pointer Track-Switching (O(N + M) Time, O(1) Space)

The "Track-Switch" insight elegantly eliminates the need to calculate lengths. If we concatenate the lists (A+B and B+A), both combined paths have the exact same length (M+N). By switching pointers to the other list's head upon reaching the end, the pointers are naturally forced to synchronize their traversal after exactly one swap, meeting at the junction node or null.

python
# Optimal: track-switching pointers
def get_intersection(headA, headB):
    p1, p2 = headA, headB
    while p1 != p2:
        p1 = p1.next if p1 else headB
        p2 = p2.next if p2 else headA
    return p1
Worked Example:Track-Switching Walkthrough

Step 1: Start of Traversal
We place pointer p1 at the head of List A (4) and p2 at the head of List B (5).

A: 4
p1
1
8
5
NULL
List A: p1 begins at head node 4.
B: 5
p2
6
1
8
5
NULL
List B: p2 begins at head node 5.

Step 2: Traversal Phase
Both pointers walk forward at identical speed. Because the paths before the intersection have different lengths, the pointers reach the intersection node 8 at different times.

A: 4
1
8
p1
5
NULL
List A: p1 reaches node 8 after 2 steps.
B: 5
6
1
p2
8
5
NULL
List B: p2 is still at node 1 after 2 steps.

Step 3: p1 Switches Tracks
When p1 reaches the end of List A (after node 5), it becomes null and immediately switches to the head of List B (5).

B: 5
p1
6
1
8
5
NULL
p1 has switched to the head of List B.
B: 5
6
1
8
5
p2
NULL
p2 has just reached node 5 of List B.

Step 4: p2 Switches Tracks
Next, p2 reaches the end of List B, becomes null, and switches to the head of List A (4).

B: 5
6
p1
1
8
5
NULL
p1 is now at node 6 of List B.
A: 4
p2
1
8
5
NULL
p2 has switched to the head of List A (node 4).

Step 5: Synchronization & Meeting
Because both pointers have now traveled the exact same combined prefix distance (Length(A) + Length(B)), their offsets are perfectly synchronized. They march forward and meet at the intersection node 8!

B: 5
6
1
8
p1
5
NULL
p1 reaches the intersection node 8 of List B.
A: 4
1
8
p2
5
NULL
p2 simultaneously reaches the intersection node 8 of List A. Junction found!
Interactive Strategy Visualization

Phase 1: Initial Sweep

Strategy: Phase-Alignment Sweep

A0
A1
B0
B1
B2
B3
B4
I0
I1
I2
STARTING...
Explanation

Both pointers pA and pB start traversing. Notice that List A is much shorter than List B.

Pro Tip: Path Symmetry

Switching tracks ensures both pointers traverse exactly A + B nodes. This mathematical guarantee forces them to meet at the junction.

Time Complexity
O(N + M)

Total distance traversed is constant.

Space Complexity
O(1)

Two pointers, zero extra memory.

Core Trick
Track Switching

Neutralize depth differences.

O(N · M) Brute Force
O(N + M) Time · O(N) Space Hash Set
O(N + M) Time · O(1) Space Two-Pointer Switch