Intersection of Two Linked Lists: Two-Pointer Length Equalization
Intersection of Two Linked Lists is LeetCode problem 160. It asks you to find the node where two singly linked lists converge into one. The brute-force approach (nested loops or hash sets) works, but the optimal solution uses a two-pointer trick that eliminates the length difference between the lists without ever counting nodes explicitly.
The problem
You are given the heads of two singly linked lists, headA and headB. Return the node at which the two lists intersect. If the two lists have no intersection, return null.
The intersection is defined by reference, not by value. If a node in listA is the exact same object as a node in listB, that is the intersection point. From that node onward, both lists share the same tail.
Example: listA = [4, 1, 8, 4, 5], listB = [5, 6, 1, 8, 4, 5]. The intersection starts at the node with value 8.
Notice that listA has a prefix of length 2 and listB has a prefix of length 3 before they merge. That length mismatch is the entire challenge of this problem.
Why the length difference matters
If both lists were the same length, you could simply walk two pointers forward in lockstep and check for equality at each step. But when the lists have different lengths, the pointers are out of sync. One pointer enters the shared portion earlier than the other, so they never land on the same node at the same time.
You need a way to compensate for the difference in prefix lengths. The classic approach is to measure both lengths, compute the difference, and advance the longer list's pointer by that many steps. But there is an even more elegant technique that avoids measuring lengths entirely.
The approach: two-pointer path equalization
Here is the key insight. If pointer A walks through all of listA and then continues from the head of listB, and pointer B walks through all of listB and then continues from the head of listA, they will both travel the exact same total distance before meeting at the intersection node.
Why? Let a be the number of nodes in listA's unique prefix, b be the number of nodes in listB's unique prefix, and c be the number of shared nodes.
- Pointer A travels:
a + c + bnodes (all of listA, then listB's prefix up to the intersection) - Pointer B travels:
b + c + anodes (all of listB, then listA's prefix up to the intersection)
Both paths have length a + b + c. Since addition is commutative, the total is the same regardless of which list each pointer starts with. After traveling the same total distance, both pointers arrive at the intersection node on the same step.
If there is no intersection, both pointers reach null at the same time (after traversing a + b and b + a nodes respectively). The null equality check catches this case and you return null.
Visual walkthrough
Let's trace through listA = [4, 1, 8, 4, 5] and listB = [5, 6, 1, 8, 4, 5]. Watch pA (indigo) and pB (amber) as they traverse both lists.
Start: pA at headA, pB at headB
pA begins at headA (node 4). pB begins at headB (node 5). They will traverse different lengths.
Step 1: Advance both pointers
pA moves to node 1. pB moves to node 6.
Step 2: pA enters shared portion
pA reaches node 8 (shared). pB reaches node 1 (still in B's prefix). They are not at the same node.
Step 3: Both in shared but at different offsets
pA is at node 4 (shared). pB is at node 8 (shared). Still different nodes.
Step 4: Still offset by 1
pA is at node 5 (last shared). pB is at node 4 (shared). Almost at the end.
Step 5: pA hits null, pB finishes shared portion
pA reached null (end of listA). pB is at node 5 (last shared). pA will redirect to headB.
Step 6: pA redirected to headB. pB hits null.
pA starts walking listB from node 5. pB reached null. pB will redirect to headA.
Step 7: pB redirected to headA. Both on second pass.
pA at node 6 in listB. pB starts walking listA from node 4. The length difference is now equalized.
Step 8: Pointers are converging
pA at node 1 in listB. pB at node 1 in listA. One more step to the shared portion.
Step 9: pA and pB meet at node 8!
Both pointers arrive at node 8 at the same time. Intersection found! Return node 8.
After pA finishes listA, it jumps to headB. After pB finishes listB, it jumps to headA. The redirect equalizes the path lengths, and they converge at node 8 on step 9.
The solution
def getIntersectionNode(headA, headB):
pA = headA
pB = headB
while pA is not pB:
pA = pA.next if pA else headB
pB = pB.next if pB else headA
return pA
That is the entire solution. Let's break it down:
- Initialize:
pAstarts atheadA,pBstarts atheadB. - Loop condition:
pA is not pBchecks reference equality. The loop runs until both pointers point to the same node (or both arenull). - Advance pA: If
pAis notnull, move it forward. If it isnull(reached the end of its current list), redirect it to the head of the other list. - Advance pB: Same logic, redirecting to
headAwhen it reaches the end of listB. - Return: When the loop exits,
pAandpBare either both at the intersection node or bothnull.
The condition uses pA (truthiness of the pointer), not pA.next. This means the redirect happens after pA has already stepped past the last node to null. That extra "null step" is important. It ensures the math works out to a + c + 1 + b and b + c + 1 + a total steps, keeping both pointers synchronized.
Complexity analysis
| Metric | Value | Reasoning |
|---|---|---|
| Time | O(m + n) | Each pointer traverses at most m + n nodes, where m and n are the lengths of the two lists. |
| Space | O(1) | Only two pointer variables. No hash set, no extra data structure. |
The hash set approach also runs in O(m + n) time but costs O(m) or O(n) space. The two-pointer technique matches the time complexity while dropping space to constant. That is the version interviewers want.
The building blocks
This problem is built from one core building block.
Two-pointer path equalization
When two sequences of different lengths share a common suffix, you can equalize the traversal by having each pointer walk both sequences. After one full pass, the pointer that started on the shorter sequence redirects to the longer one, and vice versa. The total distance traveled by each pointer becomes identical, so they arrive at the shared portion at the same time.
This building block is a specialized form of the two-pointer technique. Instead of using speed differences (like Floyd's cycle detection) or narrowing from both ends (like the classic two-pointer pattern on sorted arrays), it uses path concatenation to eliminate a length offset.
The same equalization idea appears in other contexts:
- Linked List Cycle II (LeetCode 142) uses a related length-offset argument. After detecting the cycle, resetting one pointer to the head and advancing both at the same speed causes them to meet at the cycle entry. The math behind why that works involves the same "total distance" reasoning.
- Remove Nth Node From End (LeetCode 19) uses a two-pointer offset. Advancing the lead pointer n steps first creates a fixed gap, then moving both pointers together causes the trailing pointer to land at the right position. Different setup, same core idea of manufacturing a specific offset between two pointers.
Edge cases
- No intersection. Both pointers reach
nullafter traversinga + bandb + anodes.pA is not pBbecomesnull is not null, which isFalse. The loop exits and returnsnull. Correct. - One list is empty. If
headAisnull,pAimmediately redirects toheadB, butpBwalks through listB and then redirects toheadA(which isnull). Both end up atnull. Returnsnull. Correct. - Same length lists. The redirect never matters. Both pointers reach the intersection (or
null) on their first pass. The algorithm still works, just finishes faster. - Intersection at the head. If
headAandheadBare the same node, the loop conditionpA is not pBis immediatelyFalse. Returns the head node. Correct. - One list is entirely a prefix of the other. For example, listB starts at the second node of listA. The shared suffix is all of listB. The equalization still works because the path lengths are
a + bandb + a.
From understanding to recall
This problem has a solution that fits in five lines of code, but the details are subtle. Do you check pA or pA.next for the redirect? Do you use == or is? What happens when there is no intersection? These are the kinds of details that slip under pressure.
The only reliable way to retain this is to type it from scratch, repeatedly, at spaced intervals. After a few reps, the redirect logic and the while pA is not pB loop become automatic.
Related posts
- Linked List Cycle - Floyd's tortoise and hare algorithm for cycle detection, another two-pointer technique on linked lists
- Merge Two Sorted Lists - The fundamental merge operation using two pointers on linked lists, with the dummy head technique