DSAPrep
MediumStack

Asteroid Collision

You are given an array asteroids of integers representing asteroids in a row. The indices of the asteroids in the array represent their relative position in space.

For each asteroid, the absolute value represents its size, and the sign represents its direction (positive meaning right, negative meaning left). Each asteroid moves at the same speed.

Find out the state of the asteroids after all collisions. If two asteroids meet, the smaller one will explode. If both are the same size, both will explode. Two asteroids moving in the same direction will never meet.

Example 1

            Input: asteroids = [5,10,-5]
            Output: [5,10]
            

            
                Explanation: 10 and -5 collide, and 10 survives. 5 and 10 never collide.
              
          

Example 2

            Input: asteroids = [8,-8]
            Output: []
            

            
                Explanation: 8 and -8 collide; equal sizes โ€” both explode.
              
          

Example 3

            Input: asteroids = [10,2,-5]
            Output: [10]
            

            
                Explanation: 2 and -5 collide, and -5 survives; then 10 and -5 collide, and 10 survives.
              
          

Constraints

  • 2 <= asteroids.length <= 10^4
  • -1000 <= asteroids[i] <= 1000
  • asteroids[i] != 0
View original on LeetCode โ†—

The collision rule has an order built into it. Two asteroids only ever fight when a left-mover (negative) has a right-mover (positive) somewhere to its left: two right-movers, two left-movers, or a left-mover standing to the left of a right-mover all cruise at the same speed and never touch. That means a right-mover can only be destroyed by a later left-mover, and a left-mover must engage the right-movers in front of it in left-to-right order โ€” nearest one first. A stack captures exactly that chain: the top is always the nearest survivor, so it is always the next to fight.

Brute Force: Repeated Passes

Time O(nยฒ)Space O(n)

Apply the collision rule directly to the array, over and over. Sweep left to right; whenever two adjacent asteroids form a (right-mover, left-mover) pair, resolve the collision with the same size rules and consume both slots. Survivors go into a fresh array. One sweep is never enough: the survivor of a pair can become adjacent to a defender further left, forming a brand-new collision that only the next sweep sees. Keep sweeping until a full pass changes nothing.

class Solution:
def asteroidCollision(self, asteroids: list[int]) -> list[int]:
changed = True
while changed:
changed = False
nxt = []
i = 0
while i < len(asteroids):
# A colliding pair is always "right-mover, then left-mover".
if (
i + 1 < len(asteroids)
and asteroids[i] > 0
and asteroids[i + 1] < 0
):
left, right = asteroids[i], asteroids[i + 1]
if left == -right: # equal sizes: both explode
i += 2
elif left > -right: # left survives: keep it
nxt.append(left)
i += 2
else: # right survives: keep it
nxt.append(right)
i += 2
changed = True
else:
nxt.append(asteroids[i])
i += 1
asteroids = nxt
return asteroids

Why it is quadratic: each sweep is O(n) work, but collisions can chain across sweeps. In the worst case a single big left-mover eats the right-movers one at a time โ€” e.g. [10, 9, ..., 1, -100] advances the -100 one slot left per sweep โ€” so the loop can run O(n) times for a total of O(nยฒ). Rebuilding the array every pass also pays O(n) space per sweep.

Stack Simulation

OptimalTime O(n)Space O(n)

Scan the array once. A right-mover cannot be hit from behind and cannot catch anything ahead, so push it and move on. A left-mover immediately goes to work, and the work is the whole algorithm: while the stack top is a weaker right-mover, pop it (it explodes) and keep resolving; if the top is equal, pop it and the incoming dies in the same round; if the top is stronger, the incoming explodes; and if the incoming outlasts the entire stack, push it โ€” it becomes inert, since no later asteroid can ever catch a finished left-mover. Whatever remains in the stack, bottom to top, is the answer.

class Solution:
def asteroidCollision(self, asteroids: list[int]) -> list[int]:
stack = []
for a in asteroids:
# Only a left-mover meeting a right-mover can collide.
while stack and stack[-1] > 0 and a < 0:
if stack[-1] == -a: # equal sizes: both explode
stack.pop()
a = 0
elif stack[-1] < -a: # top is smaller: top explodes, keep resolving
stack.pop()
else: # top is bigger: incoming explodes
a = 0
if a != 0:
stack.append(a)
return stack

Trace the file example, asteroids = [10, 2, -5] โ€” the two-round cascade that shows why the while loop exists:

start

Input

โ†’10โ†’2โ†5

Stack

empty

collisions only happen when an incoming โ† (negative) meets the top of the โ†’ (positive) stack โ€” one while-loop round per top

1 / 6
currentseencomparingdiscardedresult

Collision only when a left-mover (negative) meets a right-mover (positive) to its left. Scan left to right, keep alive right-movers in a stack, and let each arriving left-mover fight the top one at a time. The stack starts empty.

Then asteroids = [8, -8] โ€” the equal-size branch, where one round destroys both fighters:

start

Input

โ†’8โ†8

Stack

empty

collisions only happen when an incoming โ† (negative) meets the top of the โ†’ (positive) stack โ€” one while-loop round per top

1 / 4
currentseencomparingdiscardedresult

Second example, [8, -8]. 8 heads right from the earlier position, -8 heads left from the later one; equal speeds and equal distances put them head-on, so they meet in the middle.

Why it is linear: every asteroid is pushed at most once and popped at most once โ€” the cascade of pops is paid for by the elements it destroys โ€” so the amortized work per asteroid is O(1), giving O(n) time overall. The stack is the output holder, so worst-case space is O(n) (e.g. every asteroid moving right). The survivors keep their original left-to-right order, so the stack is the answer as-is.