Intersection Point of Two Linked Lists in DSA Go - Time & Space Complexity
We want to know how long it takes to find where two linked lists meet.
How does the time grow when the lists get longer?
Analyze the time complexity of the following code snippet.
func getIntersectionNode(headA, headB *ListNode) *ListNode {
if headA == nil || headB == nil {
return nil
}
a, b := headA, headB
for a != b {
if a == nil {
a = headB
} else {
a = a.Next
}
if b == nil {
b = headA
} else {
b = b.Next
}
}
return a
}
This code finds the node where two linked lists meet by switching pointers.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Moving pointers through each list node.
- How many times: Each pointer moves at most twice through the lists.
As the lists get longer, the pointers travel through all nodes at most twice.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 pointer moves |
| 100 | About 200 pointer moves |
| 1000 | About 2000 pointer moves |
Pattern observation: Operations grow roughly twice the total length of both lists combined.
Time Complexity: O(m + n)
This means the time grows linearly with the total number of nodes in both lists.
[X] Wrong: "The loop runs forever if there is no intersection."
[OK] Correct: The pointers switch lists and eventually both become nil, ending the loop.
Understanding this helps you show how to handle linked lists smartly and efficiently in real problems.
"What if we used extra memory to store visited nodes? How would the time complexity change?"
Practice
intersection point mean in the context of two singly linked lists?Solution
Step 1: Understand linked list nodes and references
Each node in a linked list has a reference (pointer) to the next node. Two lists intersect if they share the same node reference at some point.Step 2: Define intersection point
The intersection point is the node where both lists merge and continue as one, sharing the same reference.Final Answer:
The node where both linked lists share the same reference, meaning they merge. -> Option DQuick Check:
Intersection means shared node reference [OK]
- Confusing same value with same reference
- Thinking intersection is at the start of lists
- Assuming intersection means equal length
Solution
Step 1: Understand Go pointer usage for linked lists
Linked list nodes are usually handled via pointers to allow traversal and modification. So parameters should be pointers.Step 2: Check return type for intersection node
The function should return a pointer to the intersecting node or nil if none, so return type is *ListNode.Final Answer:
func getIntersectionNode(headA *ListNode, headB *ListNode) *ListNode -> Option CQuick Check:
Use pointers for nodes and return pointer [OK]
- Passing nodes by value instead of pointer
- Returning wrong type like int instead of node pointer
- Mixing pointer and non-pointer parameters
type ListNode struct {
Val int
Next *ListNode
}
func main() {
common := &ListNode{Val: 8, Next: &ListNode{Val: 10, Next: nil}}
listA := &ListNode{Val: 3, Next: &ListNode{Val: 7, Next: common}}
listB := &ListNode{Val: 99, Next: &ListNode{Val: 1, Next: common}}
intersection := getIntersectionNode(listA, listB)
if intersection != nil {
fmt.Println(intersection.Val)
} else {
fmt.Println(-1)
}
}
func getIntersectionNode(headA, headB *ListNode) *ListNode {
nodes := make(map[*ListNode]bool)
for headA != nil {
nodes[headA] = true
headA = headA.Next
}
for headB != nil {
if nodes[headB] {
return headB
}
headB = headB.Next
}
return nil
}Solution
Step 1: Understand the linked lists and common node
listA: 3 -> 7 -> 8 -> 10 listB: 99 -> 1 -> 8 -> 10 The common node starts at value 8.Step 2: Trace getIntersectionNode function
The function stores all nodes of listA in a map. Then it checks nodes of listB. The first node in listB found in map is the intersection: node with value 8.Final Answer:
8 -> Option BQuick Check:
First shared node value = 8 [OK]
- Printing value of last node instead of intersection
- Confusing node value with node reference
- Returning nil when intersection exists
func getIntersectionNode(headA, headB *ListNode) *ListNode {
lengthA, lengthB := 0, 0
currA, currB := headA, headB
for currA != nil {
lengthA++
currA = currA.Next
}
for currB != nil {
lengthB++
currB = currB.Next
}
diff := lengthA - lengthB
if diff < 0 {
diff = -diff
for i := 0; i < diff; i++ {
headA = headA.Next
}
} else {
for i := 0; i < diff; i++ {
headB = headB.Next
}
}
for headA != nil && headB != nil {
if headA == headB {
return headA
}
headA = headA.Next
headB = headB.Next
}
return nil
}Solution
Step 1: Analyze pointer advancement logic
If lengthA < lengthB (diff < 0), the shorter list is A, so we should advance headB to align lengths, but code advances headA incorrectly.Step 2: Identify correct pointer to advance
When diff < 0, advance headB by |diff| steps, else advance headA. The code does the opposite.Final Answer:
The function incorrectly advances headA when diff < 0 instead of headB. -> Option AQuick Check:
Advance pointer of longer list, not shorter [OK]
- Advancing pointer of shorter list instead of longer
- Not taking absolute value of length difference
- Ignoring empty list cases
Solution
Step 1: Understand two-pointer technique
Two pointers start at heads of each list. When a pointer reaches end, it switches to the other list's head. They meet at intersection or nil.Step 2: Analyze code snippets
func getIntersectionNode(headA, headB *ListNode) *ListNode { if headA == nil || headB == nil { return nil } pA, pB := headA, headB for pA != pB { if pA == nil { pA = headB } else { pA = pA.Next } if pB == nil { pB = headA } else { pB = pB.Next } } return pA } correctly implements this logic with pointer switching and loop until pointers meet. func getIntersectionNode(headA, headB *ListNode) *ListNode { visited := make(map[*ListNode]bool) for headA != nil { visited[headA] = true headA = headA.Next } for headB != nil { if visited[headB] { return headB } headB = headB.Next } return nil } uses extra space (map). func getIntersectionNode(headA, headB *ListNode) *ListNode { lengthA, lengthB := 0, 0 currA, currB := headA, headB for currA != nil { lengthA++ currA = currA.Next } for currB != nil { lengthB++ currB = currB.Next } diff := lengthA - lengthB if diff < 0 { diff = -diff for i := 0; i < diff; i++ { headA = headA.Next } } else { for i := 0; i < diff; i++ { headB = headB.Next } } for headA != nil && headB != nil { if headA == headB { return headA } headA = headA.Next headB = headB.Next } return nil } uses length difference method. func getIntersectionNode(headA, headB *ListNode) *ListNode { for headA != nil { for headB != nil { if headA == headB { return headA } headB = headB.Next } headA = headA.Next } return nil } is inefficient nested loops.Final Answer:
Option A code snippet correctly implements two-pointer technique without extra space. -> Option AQuick Check:
Two-pointer switch method = func getIntersectionNode(headA, headB *ListNode) *ListNode { if headA == nil || headB == nil { return nil } pA, pB := headA, headB for pA != pB { if pA == nil { pA = headB } else { pA = pA.Next } if pB == nil { pB = headA } else { pB = pB.Next } } return pA } [OK]
- Using extra space instead of two-pointer method
- Not switching pointers after reaching list end
- Using nested loops causing O(n^2) time
Ready to prove it?
Pass the test and this pattern moves unknown
