Asteroid Collision

This question feels like a game, and that is the fun part. Asteroids fly and crash into each other. The trick is to notice that only the most recent asteroid on one side can take the next hit. That is a perfect job for a stack. So the interviewer is checking if you can model a chain of events cleanly.

🎯 The Problem

You get a row of asteroids as numbers. You return the asteroids that survive all the crashes.

  • The size of each number is how big the asteroid is.
  • The sign is the direction. Positive moves right, negative moves left.
  • All of them move at the same speed.
  • A crash happens only when a right-mover meets a left-mover. So a positive number followed by a negative number can collide.
  • In a crash the smaller asteroid explodes. Same size means both explode.
  • Two asteroids moving the same way never meet, so they never crash.
Input: asteroids = [5, 10, -5]
Output: [5, 10]
Explanation: 10 moves right and -5 moves left, so they meet.
10 is bigger than 5, so -5 explodes and 10 survives.
5 and 10 both move right, so they never collide.

Here is the row of asteroids with their directions. Watch the spot where 10 meets -5.

5 ->

10 ->

-5 <-

10 meets -5: -5 is smaller, so -5 explodes

🐒 Approach 1: Repeated Passes (Brute Force)

Scan the whole row, resolve one crash, then start over from the beginning.

The idea:

  • Walk the row looking for a positive number right before a negative one.
  • Resolve that crash and remove whoever explodes.

How it works:

  • After a crash, restart the scan from the front.
  • Removing asteroids can create a brand new neighbor pair that now collides.
  • Keep scanning until a full pass finds no crash.

Why it is weak:

  • Every single crash makes you rescan the whole row.
  • Many crashes means many full passes.
  • Time grows toward O(nΒ²). You redo work you already did.

Here is the repeated-pass code:

asteroid_collision_repeated_passes.py
def asteroid_collision(asteroids):
changed = True
while changed:
changed = False
result = []
i = 0
while i < len(asteroids):
if i + 1 < len(asteroids) and asteroids[i] > 0 and asteroids[i + 1] < 0:
changed = True
if asteroids[i] > -asteroids[i + 1]:
result.append(asteroids[i])
elif asteroids[i] < -asteroids[i + 1]:
result.append(asteroids[i + 1])
i += 2
else:
result.append(asteroids[i])
i += 1
asteroids = result
return asteroids

⚑ Approach 2: One Pass With a Stack (Best)

The idea in one line: keep a stack of survivors, since only the newest survivor on top can take the next hit.

The idea:

  • A stack is a pile where you add and remove from the top.
  • The top holds the newest survivor, the one that can be hit next.

How a crash is handled:

  • A crash needs the top to be positive and the current asteroid to be negative.
  • Compare absolute sizes, the number without its sign.
  • Top smaller: pop it, the current asteroid keeps going, so loop.
  • Same size: pop the top and drop the current one.
  • Top bigger: drop the current one.

How it finishes:

  • If the current asteroid survives every crash, push it.
  • At the end the stack holds the survivors in order.

Why it is fast:

  • Each asteroid is pushed at most once and popped at most once.
  • So the whole thing is one pass, which is O(n).

Here is the stack changing as we process [5, 10, -5]. The stack always holds the current survivors.

push 5 -> stack [5]

push 10 -> stack [5, 10]

see -5: top 10 is positive, -5 is negative -> they meet

10 is bigger than 5, so -5 explodes -> stack stays [5, 10]

Survivors = [5, 10]

Steps to Solve

  1. Create an empty stack to hold surviving asteroids.
  2. Go through each asteroid in order.
  3. Assume the current asteroid is alive. While the stack top is positive and the current asteroid is negative, a crash can happen.
  4. Compare absolute sizes. Pop the smaller one. If equal, pop the top and mark the current one dead too.
  5. If the top is bigger, the current asteroid dies. Stop the loop.
  6. If the current asteroid is still alive after the loop, push it onto the stack.
  7. After the scan, the stack holds the survivors in order.

This Python version uses a list as the stack and the abs function to compare sizes.

asteroid_collision.py
def asteroid_collision(asteroids):
stack = [] # surviving asteroids
for cur in asteroids:
alive = True
# crash only when top moves right and cur moves left
while alive and stack and stack[-1] > 0 and cur < 0:
if stack[-1] < -cur: # top smaller, it explodes
stack.pop()
elif stack[-1] == -cur: # same size, both explode
stack.pop()
alive = False
else: # top bigger, cur explodes
alive = False
if alive:
stack.append(cur) # current asteroid survives
return stack
asteroids = [5, 10, -5]
print(asteroid_collision(asteroids))

The output of the above code will be:

[5, 10]

Let us read the Python version line by line so the collision logic is clear.

stack = [] starts an empty list that holds the survivors so far. The newest survivor is always at the end, and that is the one that can take the next hit.

for cur in asteroids: walks through every asteroid in order.

alive = True assumes the current asteroid lives. We will turn this off only if it explodes.

while alive and stack and stack[-1] > 0 and cur < 0: is the heart of it. A crash needs the top to move right, which means stack[-1] > 0, and the current asteroid to move left, which means cur < 0. If any of these is false, no crash, so we leave the loop.

if stack[-1] < -cur: compares sizes. Here -cur is the size of the left-mover, because cur is negative. If the top is smaller, the top explodes, so stack.pop() removes it. The current asteroid lives on and the loop checks the new top.

elif stack[-1] == -cur: handles the tie. Same size means both explode. We pop the top and set alive = False.

else: means the top is bigger. So the current asteroid explodes. We set alive = False and the loop ends.

if alive: stack.append(cur) pushes the current asteroid only if it survived every crash. After the full scan, the list holds the survivors in their original left-to-right order.

⏱️ Time and Space Complexity

The naive repeated-pass idea rescans the whole row after each crash, so it drifts to O(nΒ²) time. The stack version pushes and pops each asteroid at most once. So it runs in O(n) time. The stack can hold up to n asteroids, so it needs O(n) extra memory.

Approach Time Complexity Space Complexity
Repeated passes (brute force) O(nΒ²) O(n)
One pass with a stack (best) O(n) O(n)

Tip

The only collision case is a positive on top of the stack meeting a negative current asteroid. Write that condition first and the rest of the code becomes short and clear.

🧩 Key Takeaways

  • βœ… Sign means direction. Positive moves right and negative moves left.
  • βœ… A crash happens only when the stack top is positive and the current asteroid is negative.
  • βœ… Compare absolute sizes. The smaller one explodes, and equal sizes blow up both.
  • βœ… A surviving current asteroid may keep crashing into the new top, so loop until it is settled.
  • βœ… Each asteroid is pushed and popped at most once, so the whole pass is O(n).

Check Your Knowledge

4 questions Show quiz Hide quiz

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does the sign of an asteroid number mean?

    Why: The sign is the direction. Positive asteroids move right and negative ones move left.

  2. 2

    When can two asteroids actually collide?

    Why: A crash needs a positive (right) asteroid meeting a negative (left) asteroid coming toward it.

  3. 3

    In the stack solution, when does the current asteroid get pushed?

    Why: We push the current asteroid only after it survives all possible crashes with the stack top.

  4. 4

    What is the time complexity of the stack solution?

    Why: Each asteroid is pushed and popped at most once, so the whole pass is O(n).

πŸš€ What’s Next?