L12: Find the Intersection Point of Y Linked Lists
Finding the intersection point of two singly linked lists (often referred to as a "Y-shaped Linked List") is a classic algorithm and data structures problem frequently asked in technical interviews. The objective is to determine the exact node where two separate linked lists merge into a single shared sequence, or return null if they never intersect.
1. Problem Statement & Conceptual Overview
Given the head nodes of two singly linked lists (head1 and head2), our task is to find and return the first common node where the two lists merge.
Visualizing the Y-Shape
Key Behavioral Scenarios
- Intersecting Lists: The two lists run independently for a period and then merge into a single shared tail. We must return the exact memory address of the first node where they merge.
- Non-Intersecting (Parallel/Diverging) Lists: The lists never touch or collide. In this case, the algorithm must return
null. - Identical Node Values vs. Node Identity: Note that we are looking for the same node instance (memory address), not just nodes with identical integer data values. Two different nodes might both hold the value
4, but that does not make them an intersection point.
2. Approach 1: The Brute-Force Hashing Solution
The most intuitive, brute-force way to approach this problem is to use extra memory to memorize the nodes of the first list, then scan the second list to find the first match.
Algorithm Steps
- Initialize a pointer
temp1athead1and an auxiliary hash map (or hash set). - Traverse the entire first linked list (
head1), storing every node object (not its integer value) into the hash map. - Reset a pointer
temp2tohead2and traverse the second linked list. - For each node visited in the second list, check if it already exists in the hash map.
- The first node you encounter that exists in the map is your intersection point. Return it immediately.
- If you reach
nullwithout finding a match, returnnull.
Pseudo-Code Implementation
def getIntersectionNode_hashing(head1, head2):
node_map = {}
temp1 = head1
# Store all nodes of list 1 in the map
while temp1 is not None:
node_map[temp1] = True
temp1 = temp1.next
temp2 = head2
# Check nodes of list 2 against the map
while temp2 is not None:
if temp2 in node_map:
return temp2 # First common node found
temp2 = temp2.next
return None # No intersection
Complexity Analysis
- Time Complexity: , where and are the lengths of the two linked lists. If using a hash set with average insertions and lookups, the overall time complexity is .
- Space Complexity: (or depending on which list you store) because we allocate extra memory for the hash map to store node references.
Note: While this approach works, interviewers will typically reject it because of the auxiliary space complexity. We need to optimize the space complexity down to .
3. Approach 2: The Length-Difference Optimization (Two-Pointer Technique)
To solve the problem with constant extra space (), we must align the starting positions of both pointers so they reach the intersection node simultaneously.
Core Mental Model
If two runners are running at the same speed, but one starts further back than the other, they will never finish or align correctly unless we compensate for the head start. By calculating the exact length of both lists, we can determine the difference in length and advance the pointer of the longer list forward by that exact difference.
Step-by-Step Evolution
- Traverse
head1to compute its length . - Traverse
head2to compute its length . - Find the absolute difference: .
- Take two pointers (
t1andt2) and place them athead1andhead2respectively. - Move the pointer of the longer list forward by steps.
- Now, advance both pointers simultaneously node-by-node. The first node where
t1 == t2is your intersection point.
Implementation Logic
def getIntersectionNode_lengthDiff(head1, head2):
# Step 1: Compute lengths
n1, n2 = 0, 0
t1, t2 = head1, head2
while t1:
n1 += 1
t1 = t1.next
while t2:
n2 += 1
t2 = t2.next
t1, t2 = head1, head2
# Step 2: Advance the pointer of the longer list
if n1 < n2:
d = n2 - n1
for _ in range(d):
t2 = t2.next
else:
d = n1 - n2
for _ in range(d):
t1 = t1.next
# Step 3: Simultaneous traversal until intersection
while t1 != t2:
t1 = t1.next
t2 = t2.next
return t1 # Returns intersection node or None
Complexity Analysis
- Time Complexity: , which simplifies to because we traverse both lists to find lengths, traverse the difference, and finally traverse up to the intersection.
- Space Complexity: as only a few pointer variables are used.
4. Approach 3: The Optimal Two-Pointer Switching Technique
Can we avoid calculating lengths entirely and still achieve time and space? Yes! This is the most elegant solution expected in advanced interviews.
Core Mental Model: The Track Lap Analogy
Imagine two runners running around two tracks of different lengths that eventually merge into a shared track.
- Runner 1 runs around Track 1 (length ) and when they hit the end, they are instantly transported to the start of Track 2.
- Runner 2 runs around Track 2 (length ) and when they hit the end, they are instantly transported to the start of Track 1.
- Because both runners travel a total distance equal to before meeting, they are guaranteed to arrive at the intersection point at the exact same time, perfectly neutralizing the length discrepancy!
Step-by-Step Flow Walkthrough
- Initialize
temp1 = head1andtemp2 = head2. - Run a loop while
temp1 != temp2:- Move
temp1forward (temp1 = temp1.next). Iftemp1hitsnull, redirect it tohead2. - Move
temp2forward (temp2 = temp2.next). Iftemp2hitsnull, redirect it tohead1.
- Move
- If the lists intersect,
temp1andtemp2will meet at the intersection node and terminate the loop. - If the lists do not intersect, both pointers will traverse both lists, reach
nullsimultaneously at the end of their second pass, and exit the loop returningnull.
5. Complete Code Implementations (Optimal Approach)
Here is the robust, production-grade implementation of the optimal pointer-switching algorithm across popular programming languages.
Java Implementation
public class Solution {
public ListNode getIntersectionNode(ListNode head1, ListNode head2) {
// Edge case: if either list is empty, there is no intersection
if (head1 == null || head2 == null) return null;
ListNode temp1 = head1;
ListNode temp2 = head2;
// Traverse until both pointers meet at the intersection or null
while (temp1 != temp2) {
// Move temp1 to next, or switch to head2 if end is reached
temp1 = (temp1 != null) ? temp1.next : head2;
// Move temp2 to next, or switch to head1 if end is reached
temp2 = (temp2 != null) ? temp2.next : head1;
}
// Returns either the intersection node or null (if no collision)
return temp1;
}
}
Python Implementation
class Solution:
def getIntersectionNode(self, head1: ListNode, head2: ListNode) -> ListNode:
if not head1 or not head2:
return None
temp1 = head1
temp2 = head2
while temp1 != temp2:
temp1 = temp1.next if temp1 else head2
temp2 = temp2.next if temp2 else head1
return temp1
6. Real-World Practical Pitfalls & Edge Cases
When writing or testing this code in production, keep these edge cases in mind:
Summary and Key Takeaways
- Problem Definition: Finding the first common memory reference (node) where two singly linked lists merge into a Y-shape.
- Value vs. Reference: Always compare node objects/memory addresses (
temp1 == temp2), never their internal data values (temp1.val == temp2.val). - Brute-Force (Hashing): Store all nodes of the first list in a hash map, then scan the second list. Time: , Space: . Rejected by strict interviewers due to extra space.
- Length-Difference Approach: Compute lengths and , advance the longer list by the absolute difference , then march together. Time: , Space: .
- Optimal Pointer-Switching Approach: Traverse both lists simultaneously. When a pointer reaches
null, redirect it to the head of the opposite list. This elegantly neutralizes the length difference in exactly two passes. - Optimal Complexity: Time complexity is strictly and space complexity is .
- Non-Collision Handling: If no intersection exists, both pointers will traverse both lists and terminate at
nullat the exact same moment.