Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions problems/2751-robot-collisions/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# 2751. Robot Collisions

[LeetCode Link](https://leetcode.com/problems/robot-collisions/)

Difficulty: Hard
Topics: Array, Stack, Sorting, Simulation
Acceptance Rate: 57.0%

## Hints

### Hint 1

Think about which robots can actually collide. Two robots only collide if one is moving right and another (to its right) is moving left. This is similar to classic "asteroid collision" style problems. What data structure is commonly used for those?

### Hint 2

Sort the robots by position so you can process them left to right. Use a **stack** to track right-moving robots that haven't collided yet. When you encounter a left-moving robot, it may collide with the right-moving robots on the stack. Process these collisions one at a time.

### Hint 3

The key insight is handling the collision resolution loop: when a left-moving robot meets the top of the stack (a right-moving robot), compare their healths. The one with lower health is destroyed, and the survivor loses 1 health. If they're equal, both are destroyed. A single left-moving robot might destroy multiple right-moving robots from the stack before it is itself destroyed or survives. After all collisions are resolved, collect survivors and return them in their **original input order**.

## Approach

1. **Index the robots**: Create an array of indices `[0, 1, ..., n-1]` and sort them by their position. This lets us process robots left-to-right spatially while remembering their original order.

2. **Use a stack for right-movers**: Iterate through the sorted indices. If a robot moves right (`R`), push its index onto the stack. If it moves left (`L`), it could collide with right-moving robots already on the stack.

3. **Resolve collisions**: For each left-moving robot, repeatedly compare it with the top of the stack:
- If the stack's top robot has **less** health: pop it (destroyed), decrease the left-mover's health by 1, and continue checking the stack.
- If the stack's top robot has **equal** health: pop it (both destroyed), mark the left-mover as destroyed, and stop.
- If the stack's top robot has **more** health: decrease its health by 1, mark the left-mover as destroyed, and stop.
- If the stack is empty, the left-mover survives (no more right-movers to collide with).

4. **Collect survivors**: After processing all robots, the survivors are: any left-movers that weren't destroyed, plus any right-movers still on the stack. Gather their healths and return them in the original input order.

**Example walkthrough** (Example 2: positions=[3,5,2,6], healths=[10,10,15,12], directions="RLRL"):
- Sorted by position: index 2 (pos=2,L), index 0 (pos=3,R), index 1 (pos=5,L), index 3 (pos=6,L)
- Process index 2 (L): stack empty, survives.
- Process index 0 (R): push to stack. Stack: [0]
- Process index 1 (L): collide with stack top (index 0). Health 10 vs 10 => both destroyed. Stack: []
- Process index 3 (L): stack empty, survives.
- Wait -- but index 2 is at position 2 moving left, and index 3 is at position 6 moving left. They never collide with each other since they move in the same direction. The survivors are index 2 (health 15) and... let me re-check.

Actually, re-sorting: positions are [3,5,2,6] so sorted order by position is: index 2 (pos=2, dir=R), index 0 (pos=3, dir=L), index 1 (pos=5, dir=R), index 3 (pos=6, dir=L).

- Process index 2 (pos=2, R): push to stack. Stack: [2]
- Process index 0 (pos=3, L): collide with stack top (index 2, health=15). Left-mover health=10 < 15, so left-mover destroyed, stack top health becomes 14. Stack: [2]
- Process index 1 (pos=5, R): push to stack. Stack: [2, 1]
- Process index 3 (pos=6, L): collide with stack top (index 1, health=10). Left-mover health=12 > 10, so stack top destroyed, left-mover health becomes 11. Now collide with new stack top (index 2, health=14). Left-mover health=11 < 14, so left-mover destroyed, stack top health becomes 13. Stack: [2]
- Survivors: index 2 with health 13... Hmm, that doesn't match the expected output of [14].

The expected output [14] means robot 3 (0-indexed index 2, position 2, health 15) survives with health 14. Let me re-read: directions = "RLRL", so index 0='R', index 1='L', index 2='R', index 3='L'.

Sorted by position: index 2 (pos=2, dir='R'), index 0 (pos=3, dir='R'), index 1 (pos=5, dir='L'), index 3 (pos=6, dir='L').

- Index 2 (pos=2, R): push. Stack: [2]
- Index 0 (pos=3, R): push. Stack: [2, 0]
- Index 1 (pos=5, L, health=10): collide with top (index 0, health=10). Equal => both destroyed. Stack: [2]
- Index 3 (pos=6, L, health=12): collide with top (index 2, health=15). 12 < 15 => index 3 destroyed, index 2 health becomes 14. Stack: [2]
- Survivor: index 2 with health 14. Output in original order: [14]. Correct!

## Complexity Analysis

Time Complexity: O(n log n) -- dominated by the sort. Each robot is pushed and popped from the stack at most once, so the collision processing is O(n).

Space Complexity: O(n) -- for the sorted indices array and the stack.

## Edge Cases

- **All robots moving the same direction**: No collisions occur. Return all healths unchanged.
- **All robots destroyed**: Every collision results in mutual destruction. Return an empty array.
- **Single robot**: No collisions possible. Return its health.
- **Chain of collisions**: A single left-moving robot with high health destroys multiple right-moving robots in sequence, losing 1 health each time.
- **Unsorted positions**: The positions are not necessarily in order, so sorting by position is essential before processing.
76 changes: 76 additions & 0 deletions problems/2751-robot-collisions/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
number: "2751"
frontend_id: "2751"
title: "Robot Collisions"
slug: "robot-collisions"
difficulty: "Hard"
topics:
- "Array"
- "Stack"
- "Sorting"
- "Simulation"
acceptance_rate: 5703.6
is_premium: false
created_at: "2026-04-01T03:48:45.150624+00:00"
fetched_at: "2026-04-01T03:48:45.150624+00:00"
link: "https://leetcode.com/problems/robot-collisions/"
date: "2026-04-01"
---

# 2751. Robot Collisions

There are `n` **1-indexed** robots, each having a position on a line, health, and movement direction.

You are given **0-indexed** integer arrays `positions`, `healths`, and a string `directions` (`directions[i]` is either **' L'** for **left** or **' R'** for **right**). All integers in `positions` are **unique**.

All robots start moving on the line**simultaneously** at the **same speed** in their given directions. If two robots ever share the same position while moving, they will **collide**.

If two robots collide, the robot with **lower health** is **removed** from the line, and the health of the other robot **decreases** **by one**. The surviving robot continues in the **same** direction it was going. If both robots have the **same** health, they are both**** removed from the line.

Your task is to determine the **health** of the robots that survive the collisions, in the same **order** that the robots were given,**** i.e. final health of robot 1 (if survived), final health of robot 2 (if survived), and so on. If there are no survivors, return an empty array.

Return _an array containing the health of the remaining robots (in the order they were given in the input), after no further collisions can occur._

**Note:** The positions may be unsorted.





**Example 1:**

![](https://assets.leetcode.com/uploads/2023/05/15/image-20230516011718-12.png)


**Input:** positions = [5,4,3,2,1], healths = [2,17,9,15,10], directions = "RRRRR"
**Output:** [2,17,9,15,10]
**Explanation:** No collision occurs in this example, since all robots are moving in the same direction. So, the health of the robots in order from the first robot is returned, [2, 17, 9, 15, 10].


**Example 2:**

![](https://assets.leetcode.com/uploads/2023/05/15/image-20230516004433-7.png)


**Input:** positions = [3,5,2,6], healths = [10,10,15,12], directions = "RLRL"
**Output:** [14]
**Explanation:** There are 2 collisions in this example. Firstly, robot 1 and robot 2 will collide, and since both have the same health, they will be removed from the line. Next, robot 3 and robot 4 will collide and since robot 4's health is smaller, it gets removed, and robot 3's health becomes 15 - 1 = 14. Only robot 3 remains, so we return [14].


**Example 3:**

![](https://assets.leetcode.com/uploads/2023/05/15/image-20230516005114-9.png)


**Input:** positions = [1,2,5,6], healths = [10,10,11,11], directions = "RLRL"
**Output:** []
**Explanation:** Robot 1 and robot 2 will collide and since both have the same health, they are both removed. Robot 3 and 4 will collide and since both have the same health, they are both removed. So, we return an empty array, [].



**Constraints:**

* `1 <= positions.length == healths.length == directions.length == n <= 105`
* `1 <= positions[i], healths[i] <= 109`
* `directions[i] == 'L'` or `directions[i] == 'R'`
* All values in `positions` are distinct
60 changes: 60 additions & 0 deletions problems/2751-robot-collisions/solution_daily_20260401.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Robot Collisions: Sort robots by position, then use a stack to simulate
// collisions between right-moving and left-moving robots.
package main

import "sort"

func survivedRobotsHealths(positions []int, healths []int, directions string) []int {
n := len(positions)

// Create indices sorted by position
indices := make([]int, n)
for i := range indices {
indices[i] = i
}
sort.Slice(indices, func(a, b int) bool {
return positions[indices[a]] < positions[indices[b]]
})

// Stack holds indices of right-moving robots
stack := []int{}

// Track which robots are removed
removed := make([]bool, n)

for _, i := range indices {
if directions[i] == 'R' {
stack = append(stack, i)
continue
}

// directions[i] == 'L': resolve collisions with right-movers on stack
for len(stack) > 0 && !removed[i] {
top := stack[len(stack)-1]
if healths[top] < healths[i] {
// Right-mover destroyed
removed[top] = true
stack = stack[:len(stack)-1]
healths[i]--
} else if healths[top] == healths[i] {
// Both destroyed
removed[top] = true
stack = stack[:len(stack)-1]
removed[i] = true
} else {
// Left-mover destroyed
healths[top]--
removed[i] = true
}
}
}

// Collect survivors in original order
result := []int{}
for i := 0; i < n; i++ {
if !removed[i] {
result = append(result, healths[i])
}
}
return result
}
83 changes: 83 additions & 0 deletions problems/2751-robot-collisions/solution_daily_20260401_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package main

import (
"reflect"
"testing"
)

func TestSurvivedRobotsHealths(t *testing.T) {
tests := []struct {
name string
positions []int
healths []int
directions string
expected []int
}{
{
name: "example 1: all moving right, no collisions",
positions: []int{5, 4, 3, 2, 1},
healths: []int{2, 17, 9, 15, 10},
directions: "RRRRR",
expected: []int{2, 17, 9, 15, 10},
},
{
name: "example 2: two collisions, one survivor",
positions: []int{3, 5, 2, 6},
healths: []int{10, 10, 15, 12},
directions: "RLRL",
expected: []int{14},
},
{
name: "example 3: all destroyed in equal collisions",
positions: []int{1, 2, 5, 6},
healths: []int{10, 10, 11, 11},
directions: "RLRL",
expected: []int{},
},
{
name: "edge case: single robot",
positions: []int{42},
healths: []int{100},
directions: "R",
expected: []int{100},
},
{
name: "edge case: all moving left, no collisions",
positions: []int{1, 2, 3, 4, 5},
healths: []int{5, 4, 3, 2, 1},
directions: "LLLLL",
expected: []int{5, 4, 3, 2, 1},
},
{
name: "edge case: chain collision, high-health left-mover destroys many",
positions: []int{1, 2, 3, 4, 5},
healths: []int{1, 1, 1, 1, 100},
directions: "RRRRL",
expected: []int{96},
},
{
name: "edge case: right-mover survives multiple left-movers",
positions: []int{1, 2, 3, 4, 5},
healths: []int{100, 1, 1, 1, 1},
directions: "RLLLL",
expected: []int{96},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Copy inputs since the function modifies healths in place
h := make([]int, len(tt.healths))
copy(h, tt.healths)

result := survivedRobotsHealths(tt.positions, h, tt.directions)

if len(result) == 0 && len(tt.expected) == 0 {
return // both empty, pass
}
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("got %v, want %v", result, tt.expected)
}
})
}
}