160. Intersection of Two Linked Lists
Problem Description
You are given the heads of two singly linked lists, headA
and headB
. Your task is to find and return the node where these two linked lists intersect. If the two linked lists don't intersect at all, return null
.
When two linked lists intersect, they share the same nodes from the intersection point onwards. This means that from the intersection node, both lists have the exact same nodes (not just the same values, but the actual same node objects in memory).
For example, if list A is [4, 1, 8, 4, 5]
and list B is [5, 6, 1, 8, 4, 5]
, and they intersect at the node with value 8
, then from that node onwards, both lists share the nodes [8, 4, 5]
.
Important constraints:
- The linked lists have no cycles
- You must preserve the original structure of both linked lists (don't modify them)
- The intersection is based on node reference, not node value
The solution uses a two-pointer technique. Two pointers a
and b
start at headA
and headB
respectively. They traverse their lists, and when pointer a
reaches the end of list A, it continues from the head of list B. Similarly, when pointer b
reaches the end of list B, it continues from the head of list A.
The key insight is that by switching lists when reaching the end, both pointers will traverse the same total distance. If there's an intersection, they'll meet at the intersection node. If there's no intersection, both pointers will eventually become null
at the same time.
The algorithm works because:
- If lists intersect: Both pointers travel
(length of unique part of A) + (length of unique part of B) + (length of common part)
before meeting - If lists don't intersect: Both pointers travel
(length of A) + (length of B)
and becomenull
together
Intuition
The challenge is finding where two linked lists meet when we can only traverse forward and don't know their lengths beforehand. The key observation is that if two lists intersect, they share a common "tail" from the intersection point onwards.
Let's think about the structure: if list A has length m
and list B has length n
, and they intersect at some node, then:
- List A has some unique nodes before the intersection
- List B has some unique nodes before the intersection
- Both share the same nodes after the intersection
The naive approach would be to check every node in list A against every node in list B, but that's inefficient. Another approach might be to find the lengths first, then align the lists, but that requires multiple passes.
The brilliant insight comes from realizing that the difference in path lengths is what prevents two pointers from meeting at the intersection when traversing simultaneously. If we could somehow "neutralize" this length difference, the pointers would naturally meet at the intersection.
Here's the key idea: if we concatenate the traversal paths, we get:
- Pointer starting at A: travels through
A
then throughB
- Pointer starting at B: travels through
B
then throughA
Both pointers now travel the exact same total distance: length(A) + length(B)
. More importantly, they both travel through the same unique portions of each list before reaching any potential intersection point.
If the lists intersect:
- Pointer A travels:
(A's unique part) + (common part) + (B's unique part) + (common part)
- Pointer B travels:
(B's unique part) + (common part) + (A's unique part) + (common part)
- They meet after traveling
(A's unique part) + (B's unique part) + (common part)
If the lists don't intersect:
- Both pointers travel the full length of both lists and reach
null
simultaneously
This elegant solution ensures both pointers synchronize naturally without explicitly calculating lengths or making multiple passes.
Solution Approach
The implementation uses a two-pointer technique where both pointers traverse the linked lists in a specific pattern to find the intersection point.
Algorithm Steps:
-
Initialize two pointers: Set pointer
a
toheadA
and pointerb
toheadB
. -
Traverse both lists simultaneously: Move both pointers forward one node at a time using a while loop that continues as long as
a != b
. -
Handle list switching:
- When pointer
a
reaches the end of its current list (a
becomesNone
), redirect it to the head of the other list:a = headB
- When pointer
b
reaches the end of its current list (b
becomesNone
), redirect it to the head of the other list:b = headA
- If the pointer hasn't reached the end yet, simply move to the next node:
a = a.next
orb = b.next
- When pointer
-
Detect intersection or termination:
- The loop exits when
a == b
, which happens in two cases:- Both pointers meet at the intersection node (if lists intersect)
- Both pointers become
None
at the same time (if lists don't intersect)
- The loop exits when
-
Return the result: Return pointer
a
(orb
, since they're equal), which will be either the intersection node orNone
.
Implementation Details:
The concise implementation uses Python's ternary operator to handle the list switching:
a = a.next if a else headB
b = b.next if b else headA
This checks if the pointer exists (hasn't reached None
). If it exists, move to the next node; otherwise, switch to the head of the other list.
Time and Space Complexity:
- Time Complexity:
O(m + n)
wherem
andn
are the lengths of the two lists. Each pointer traverses at mostm + n
nodes. - Space Complexity:
O(1)
as we only use two pointers regardless of input size.
The beauty of this solution lies in its simplicity - no need to calculate lengths, no need for hash tables, just two pointers that eventually synchronize at the intersection point or at None
.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to see how the two-pointer technique finds the intersection.
Example Setup:
- List A:
1 → 2 → 3 → 4 → 5
- List B:
9 → 4 → 5
- These lists intersect at node
4
(the same node object, not just same value)
Visual Representation:
List A: 1 → 2 → 3 ↘ 4 → 5 → null List B: 9 ↗
Step-by-step Execution:
Initial State:
- Pointer
a
starts at node1
(head of List A) - Pointer
b
starts at node9
(head of List B)
Iteration 1: a != b
(1 != 9)
a
moves from1
to2
b
moves from9
to4
Iteration 2: a != b
(2 != 4)
a
moves from2
to3
b
moves from4
to5
Iteration 3: a != b
(3 != 5)
a
moves from3
to4
b
moves from5
tonull
Iteration 4: a != b
(4 != null)
a
moves from4
to5
b
reaches end, switches to head of List A:b
=1
Iteration 5: a != b
(5 != 1)
a
moves from5
tonull
b
moves from1
to2
Iteration 6: a != b
(null != 2)
a
reaches end, switches to head of List B:a
=9
b
moves from2
to3
Iteration 7: a != b
(9 != 3)
a
moves from9
to4
b
moves from3
to4
Iteration 8: a == b
(both at node 4)
- Loop exits
- Return node
4
(the intersection point)
Path Analysis:
- Pointer
a
traveled:1→2→3→4→5→null→9→4
(total: 7 steps to intersection) - Pointer
b
traveled:9→4→5→null→1→2→3→4
(total: 7 steps to intersection)
Both pointers traveled exactly the same distance before meeting at the intersection node! This demonstrates why the algorithm works: by switching lists when reaching the end, both pointers equalize their path lengths and naturally synchronize at the intersection point.
Solution Implementation
1# Definition for singly-linked list.
2# class ListNode:
3# def __init__(self, x):
4# self.val = x
5# self.next = None
6
7from typing import Optional
8
9
10class Solution:
11 def getIntersectionNode(self, headA: Optional[ListNode], headB: Optional[ListNode]) -> Optional[ListNode]:
12 """
13 Find the intersection node of two linked lists.
14
15 Algorithm:
16 - Use two pointers starting at the heads of both lists
17 - When a pointer reaches the end of its list, redirect it to the head of the other list
18 - Both pointers will meet at the intersection point (or None if no intersection)
19 - This works because both pointers traverse the same total distance:
20 Length of List A + Length of List B (in different orders)
21
22 Args:
23 headA: Head node of the first linked list
24 headB: Head node of the second linked list
25
26 Returns:
27 The intersection node if exists, otherwise None
28 """
29 # Initialize two pointers at the heads of both lists
30 pointer_a = headA
31 pointer_b = headB
32
33 # Continue until both pointers meet (either at intersection or both None)
34 while pointer_a != pointer_b:
35 # Move pointer_a to next node, or to headB if reached end of list A
36 pointer_a = pointer_a.next if pointer_a else headB
37
38 # Move pointer_b to next node, or to headA if reached end of list B
39 pointer_b = pointer_b.next if pointer_b else headA
40
41 # Return the intersection node (or None if no intersection exists)
42 return pointer_a
43
1/**
2 * Definition for singly-linked list.
3 * public class ListNode {
4 * int val;
5 * ListNode next;
6 * ListNode(int x) {
7 * val = x;
8 * next = null;
9 * }
10 * }
11 */
12public class Solution {
13 /**
14 * Finds the intersection node of two singly-linked lists.
15 * Uses the two-pointer technique where both pointers traverse both lists.
16 * When a pointer reaches the end of one list, it continues from the head of the other list.
17 * If there's an intersection, both pointers will meet at the intersection node.
18 * If there's no intersection, both pointers will eventually become null simultaneously.
19 *
20 * @param headA The head of the first linked list
21 * @param headB The head of the second linked list
22 * @return The intersection node if it exists, otherwise null
23 */
24 public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
25 // Initialize two pointers starting at the heads of both lists
26 ListNode pointerA = headA;
27 ListNode pointerB = headB;
28
29 // Continue traversing until both pointers meet (either at intersection or both null)
30 while (pointerA != pointerB) {
31 // If pointerA reaches the end of list A, redirect it to the head of list B
32 // Otherwise, move to the next node in the current list
33 pointerA = (pointerA == null) ? headB : pointerA.next;
34
35 // If pointerB reaches the end of list B, redirect it to the head of list A
36 // Otherwise, move to the next node in the current list
37 pointerB = (pointerB == null) ? headA : pointerB.next;
38 }
39
40 // Return the meeting point (intersection node or null if no intersection)
41 return pointerA;
42 }
43}
44
1/**
2 * Definition for singly-linked list.
3 * struct ListNode {
4 * int val;
5 * ListNode *next;
6 * ListNode(int x) : val(x), next(NULL) {}
7 * };
8 */
9class Solution {
10public:
11 /**
12 * Find the intersection node of two linked lists.
13 *
14 * Algorithm: Two-pointer technique
15 * - Both pointers traverse their respective lists and then switch to the other list
16 * - If there's an intersection, they'll meet at the intersection node
17 * - If there's no intersection, they'll both reach NULL at the same time
18 *
19 * Time Complexity: O(m + n) where m and n are the lengths of the two lists
20 * Space Complexity: O(1)
21 *
22 * @param headA: Head of the first linked list
23 * @param headB: Head of the second linked list
24 * @return: The intersection node if exists, otherwise NULL
25 */
26 ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
27 // Initialize two pointers to traverse both lists
28 ListNode* pointerA = headA;
29 ListNode* pointerB = headB;
30
31 // Continue until both pointers meet (either at intersection or NULL)
32 while (pointerA != pointerB) {
33 // If pointerA reaches the end of list A, redirect it to the head of list B
34 // Otherwise, move to the next node in the current list
35 pointerA = (pointerA != nullptr) ? pointerA->next : headB;
36
37 // If pointerB reaches the end of list B, redirect it to the head of list A
38 // Otherwise, move to the next node in the current list
39 pointerB = (pointerB != nullptr) ? pointerB->next : headA;
40 }
41
42 // Return the meeting point (intersection node or NULL if no intersection)
43 return pointerA;
44 }
45};
46
1/**
2 * Definition for singly-linked list node
3 */
4class ListNode {
5 val: number;
6 next: ListNode | null;
7
8 constructor(val?: number, next?: ListNode | null) {
9 this.val = (val === undefined ? 0 : val);
10 this.next = (next === undefined ? null : next);
11 }
12}
13
14/**
15 * Find the intersection node of two linked lists
16 *
17 * Algorithm explanation:
18 * - Two pointers traverse both lists simultaneously
19 * - When a pointer reaches the end of its list, it continues from the head of the other list
20 * - If lists intersect, pointers will meet at the intersection node
21 * - If no intersection exists, both pointers will eventually be null
22 *
23 * Time Complexity: O(m + n) where m and n are the lengths of the two lists
24 * Space Complexity: O(1) - only using two pointers
25 *
26 * @param headA - Head of the first linked list
27 * @param headB - Head of the second linked list
28 * @returns The intersection node if it exists, otherwise null
29 */
30function getIntersectionNode(headA: ListNode | null, headB: ListNode | null): ListNode | null {
31 // Initialize two pointers, one for each list
32 let pointerA: ListNode | null = headA;
33 let pointerB: ListNode | null = headB;
34
35 // Continue traversing until both pointers meet (either at intersection or both null)
36 while (pointerA !== pointerB) {
37 // Move pointer A to next node, or to head of list B if reached end
38 pointerA = pointerA ? pointerA.next : headB;
39
40 // Move pointer B to next node, or to head of list A if reached end
41 pointerB = pointerB ? pointerB.next : headA;
42 }
43
44 // Return the meeting point (intersection node or null if no intersection)
45 return pointerA;
46}
47
Time and Space Complexity
Time Complexity: O(m + n)
, where m
is the length of linked list A and n
is the length of linked list B.
The algorithm uses two pointers that traverse both linked lists. In the worst case, each pointer will traverse its own list completely and then traverse the other list until they meet at the intersection point (or both become None
if there's no intersection).
- Pointer
a
travels at most: length of list A + length of list B =m + n
- Pointer
b
travels at most: length of list B + length of list A =n + m
Since both pointers move one step at a time simultaneously, the total iterations is bounded by m + n
.
Space Complexity: O(1)
The algorithm only uses two pointers (a
and b
) to traverse the linked lists, requiring constant extra space regardless of the input size. No additional data structures like hash tables or arrays are used.
Common Pitfalls
1. Infinite Loop When Both Lists Are Empty
One common pitfall occurs when both input lists are None
(empty lists). In this case, both pointers start as None
, and the condition pointer_a != pointer_b
evaluates to False
immediately, which correctly returns None
. However, some developers might try to "optimize" by adding extra conditions that could break this edge case.
Incorrect attempt:
# Wrong: Adding unnecessary null checks that break the algorithm
def getIntersectionNode(self, headA, headB):
if not headA or not headB: # Unnecessary check
return None
pointer_a = headA
pointer_b = headB
# This could cause infinite loop if lists don't intersect
while pointer_a != pointer_b:
pointer_a = pointer_a.next if pointer_a.next else headB # Wrong!
pointer_b = pointer_b.next if pointer_b.next else headA # Wrong!
return pointer_a
Why it's wrong: Checking pointer_a.next
instead of pointer_a
means the pointers never become None
. If the lists don't intersect, the pointers will keep cycling through both lists infinitely.
2. Misunderstanding the Switching Logic
Another pitfall is incorrectly implementing the list-switching mechanism, particularly confusing when to switch lists.
Incorrect attempt:
# Wrong: Switching at the wrong time
def getIntersectionNode(self, headA, headB):
pointer_a = headA
pointer_b = headB
while pointer_a != pointer_b:
if pointer_a.next is None: # Checking next instead of current
pointer_a = headB
else:
pointer_a = pointer_a.next
if pointer_b.next is None: # Checking next instead of current
pointer_b = headA
else:
pointer_b = pointer_b.next
return pointer_a
Why it's wrong: This checks if the next node is None
rather than the current pointer. This causes the pointers to switch one step too early, breaking the distance synchronization that makes the algorithm work.
3. Modifying the Original Lists
Some might be tempted to mark visited nodes or modify the list structure to detect cycles or intersections.
Incorrect attempt:
# Wrong: Modifying the original list structure
def getIntersectionNode(self, headA, headB):
# Marking visited nodes by changing their values
visited_marker = float('inf')
current = headA
while current:
original_val = current.val
current.val = visited_marker # Modifying the list!
current = current.next
# Check if any node in list B was visited
current = headB
intersection = None
while current:
if current.val == visited_marker:
intersection = current
break
current = current.next
# Restore original values (but this is still wrong!)
# ...
return intersection
Why it's wrong: This violates the constraint of preserving the original list structure. Even if you restore values afterward, this approach fails when multiple threads access the lists concurrently.
Correct Solution Reminder
The correct implementation maintains simplicity:
def getIntersectionNode(self, headA, headB):
pointer_a = headA
pointer_b = headB
while pointer_a != pointer_b:
# Switch to the other list's head when reaching None (not before!)
pointer_a = pointer_a.next if pointer_a else headB
pointer_b = pointer_b.next if pointer_b else headA
return pointer_a
Key points to remember:
- Check the current pointer for
None
, notpointer.next
- Let both pointers become
None
naturally if there's no intersection - Never modify the original list nodes
- Trust the algorithm's elegance - both pointers will synchronize after traversing the same total distance
Which technique can we use to find the middle of a linked list?
Recommended Readings
Linked List Cycle Given a linked list with potentially a loop determine whether the linked list from the first node contains a cycle in it For bonus points do this with constant space Parameters nodes The first node of a linked list with potentially a loop Result Whether there is a loop contained
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!