Asteroid Collision — Stack Simulation for Directional Collisions
Advertisement
Problem Statement
We are given an array asteroids of integers representing asteroids in a row. For each asteroid, the absolute value represents its size and the sign represents its direction (positive = right, negative = left). Each asteroid moves at the same speed.
Find the state of the asteroids after all collisions. If two asteroids meet, the smaller one explodes. If both are the same size, both explode. Two asteroids moving in the same direction will never meet.
Constraints:
2 <= asteroids.length <= 10^4-1000 <= asteroids[i] <= 1000asteroids[i] != 0
Input: asteroids = [5,10,-5]
Output: [5,10]
Explanation: 10 and -5 collide → 10 wins. 5 and 10 never collide (same direction).Input: asteroids = [8,-8]
Output: []
Explanation: 8 and -8 collide and both explode.Input: asteroids = [10,2,-5]
Output: [10]
Explanation: 2 and -5 collide → -5 wins. Then 10 and -5 collide → 10 wins.Why This Problem Matters
LC 735 is a classic stack collision simulation problem asked at Amazon, Bloomberg, and Facebook. It tests the ability to identify when a stack is the right structure for a simulation problem involving pending collisions.
The key insight: right-moving asteroids accumulate as "pending" — they may collide with future left-moving asteroids. A stack naturally stores these pending right-movers. When a left-moving asteroid arrives, it destroys right-movers until either it is destroyed itself or no right-moving asteroids remain.
This pattern — pushing elements that may interact with future elements, and popping on interaction — is the same pattern in Valid Parentheses (bracket matching), Asteroid Collision (size comparison), and more complex variants like collision modeling in physics simulations.
The Core Insight
When does a collision happen? Only when a left-moving asteroid (negative) encounters a right-moving asteroid (positive) that was pushed to the stack earlier. Same-direction asteroids never collide.
Stack invariant: The stack always contains asteroids in a "stable" configuration — no two adjacent asteroids in the stack would collide with each other. All positive (right-moving) asteroids stay on the stack until a negative (left-moving) asteroid destroys them.
Processing each asteroid a:
- If
a > 0(right-moving): always push — it cannot collide with what is already on the stack (stack contains only stable configurations, and right-movers never collide with past asteroids). - If
a < 0(left-moving): potentially collides with the top of the stack if the top is positive:- Stack top is smaller (
stack[-1] < -a): stack top explodes (pop), asteroidasurvives, continue loop. - Stack top is equal (
stack[-1] == -a): both explode (pop, andais destroyed). - Stack top is larger (
stack[-1] > -a):aexplodes, stop. - Stack is empty or stack top is also negative: no collision, push
a.
- Stack top is smaller (
Visual Dry Run
Input: [10, 2, -5]
| Asteroid | Stack | Action |
|---|---|---|
| 10 | [] | positive → push |
| 2 | [10] | positive → push |
| -5 | [10,2] | top=2 < 5 → pop 2; top=10 > 5 → 10 survives, -5 destroyed |
Result: [10] ✓
Input: [8, -8]
| Asteroid | Stack | Action |
|---|---|---|
| 8 | [] | push |
| -8 | [8] | top=8 == 8 → both explode; pop 8, -8 destroyed |
Result: [] ✓
Input: [-2, -1, 1, 2]
| Asteroid | Stack | Action |
|---|---|---|
| -2 | [] | negative, stack empty → push |
| -1 | [-2] | negative, top=-2 also negative → push |
| 1 | [-2,-1] | positive → push |
| 2 | [-2,-1,1] | positive → push |
Result: [-2,-1,1,2] — no collisions because the negatives are moving left (away from positives).
Solution (Optimal)
# Python — stack collision simulation, O(n) time and space
def asteroidCollision(asteroids: list[int]) -> list[int]:
stack = [] # stores surviving asteroids in stable configuration
for a in asteroids:
alive = True # tracks whether asteroid 'a' survives
# Collision happens when 'a' is left-moving and top of stack is right-moving
while alive and a < 0 and stack and stack[-1] > 0:
top = stack[-1]
if top < -a:
# Right-moving asteroid is smaller → it explodes, 'a' continues
stack.pop()
elif top == -a:
# Equal size → both explode
stack.pop()
alive = False
else:
# Right-moving asteroid is larger → 'a' explodes
alive = False
if alive:
stack.append(a)
return stack// JavaScript — stack collision simulation, O(n) time and space
function asteroidCollision(asteroids) {
const stack = [];
for (const a of asteroids) {
let alive = true;
while (alive && a < 0 && stack.length > 0 && stack[stack.length - 1] > 0) {
const top = stack[stack.length - 1];
if (top < -a) {
stack.pop(); // right-moving smaller → explodes, a continues
} else if (top === -a) {
stack.pop(); // equal → both explode
alive = false;
} else {
alive = false; // right-moving larger → a explodes
}
}
if (alive) {
stack.push(a);
}
}
return stack;
}Complexity:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Stack simulation | O(n) | O(n) | Each asteroid pushed once, popped at most once |
Common Mistakes
-
Forgetting the
aliveflag. After an equal-size collision, asteroidais destroyed (not pushed). Without thealiveflag, you might push a destroyed asteroid or loop incorrectly. -
Not handling the case where the stack top is also negative. If the stack top is negative and
ais also negative, there is no collision — both are moving left. The conditionstack[-1] > 0correctly ensures we only collide with right-moving asteroids. -
Forgetting that multiple collisions can cascade. A single left-moving asteroid may destroy multiple right-moving asteroids before being destroyed itself (or surviving). The while loop handles this cascade.
-
Using
abs(a)instead of-afor size comparison. Sinceais guaranteed negative in the collision check,-ais its absolute size. Bothabs(a)and-awork, but-ais slightly faster. -
Returning stack as-is in Java without converting. Java's
Dequeiterates in LIFO order. Convert to array properly — either push to a list and convert, or use anArrayDequeand collect in insertion order.
Interview Tips
- Name the invariant: "The stack always holds a stable configuration — no two adjacent asteroids in the stack would collide. The while loop restores this invariant after each new asteroid."
- Trace through the
[10, 2, -5]example step by step — it shows the cascading collision. - Handle the equal-size case explicitly: "When sizes are equal, both explode — set
alive = Falseand pop the stack top." - Mention the amortized O(n) argument: "Each asteroid is pushed once and popped at most once → at most 2n operations total."
Follow-up Questions
- What if asteroids can also move at different speeds? Two right-movers at different speeds will eventually collide. Model with time of collision calculations — much harder.
- What if there is a wall at each end that reflects asteroids? Surviving asteroids that hit a wall change direction — extend the simulation with reflection rules.
- What if the size comparison used >= instead of >? The equal-size collision rule changes — a larger left-mover now also survives equal collisions.
- Count total asteroids destroyed. Track a counter incremented each time
stack.pop()is called, and also whenalive = Falsewithout a pop (the incoming asteroid is destroyed). - Return both survivors and the number of collisions. Track a collision counter alongside the stack.
Key Takeaways
- A stack models the "pending right-movers waiting for future left-movers" perfectly — LIFO matches the collision order (most recent right-mover collides first).
- The collision condition is:
a < 0(left-moving) ANDstack[-1] > 0(top is right-moving). Any other combination has no collision. - Three collision outcomes: right-mover explodes (pop,
acontinues), both explode (pop,alive = False), left-mover explodes (alive = Falsewithout pop). - Use an
aliveflag to track whether the current asteroid survives the cascade of collisions before being pushed. - Each asteroid is pushed at most once and popped at most once → O(n) amortized time.
- This pattern — pushing pending elements and popping on conflict — is the same as Valid Parentheses, stock span, and monotonic stack problems.
Advertisement