2751. Robot Collisions
Problem Description
You have n
robots on a line, where each robot has three properties:
- Position: where the robot is located on the line
- Health: an integer value representing the robot's health
- Direction: either 'L' (moving left) or 'R' (moving right)
The robots are given to you through three arrays:
positions[i]
- the position of roboti+1
(robots are 1-indexed but arrays are 0-indexed)healths[i]
- the health of roboti+1
directions[i]
- the direction of roboti+1
('L' or 'R')
All positions are unique, and the robots all start moving simultaneously at the same speed.
Collision Rules:
- When two robots meet at the same position (one moving right, one moving left), they collide
- When robots collide:
- If they have different health values: the robot with lower health is removed, and the surviving robot's health decreases by 1
- If they have the same health: both robots are removed
- Surviving robots continue moving in their original direction
Your Task: Return an array containing the final health values of all surviving robots, in the same order as they were given in the input. If a robot doesn't survive, it shouldn't appear in the result. If no robots survive, return an empty array.
Important Note: The positions array may not be sorted, meaning robots aren't necessarily given in left-to-right order by position.
Example Walkthrough:
If you have robots at positions [5,4,3]
with healths [2,17,9]
and directions "RLR"
:
- Robot at position 3 moves right, robot at position 4 moves left - they collide
- Robot at position 4 (health 17) survives with health 16, robot at position 3 is removed
- Robot at position 5 moves right, robot at position 4 moves left - they collide
- Robot at position 4 (health 16) survives with health 15, robot at position 5 is removed
- Final result:
[0,15,0]
→[15]
(only the second robot survives)
Intuition
The key insight is that we need to simulate the collisions as they would happen on a number line. Since all robots move at the same speed, we can think about this problem in terms of which robots will meet each other based on their positions and directions.
First, we need to process robots from left to right by position (not by their input order), because collisions happen based on spatial location, not input order. This is why we sort the indices by position.
The critical observation is that collisions only happen between robots moving in opposite directions - specifically, when a robot moving right ('R') meets a robot moving left ('L'). If we process robots from left to right:
- Robots moving right ('R') are potential collision candidates for future robots moving left
- Robots moving left ('L') will collide with any robots moving right that are to their left
This naturally suggests using a stack to track robots moving right. As we process each robot:
- If it's moving right ('R'), we add it to the stack (it might collide with future left-moving robots)
- If it's moving left ('L'), it needs to resolve collisions with all right-moving robots in its path (those in the stack)
When a left-moving robot encounters right-moving robots (in the stack), we simulate collisions one by one:
- Compare healths and update accordingly
- The robot with higher health survives (with health reduced by 1)
- If healths are equal, both are eliminated
- Continue until either the left-moving robot is eliminated or there are no more right-moving robots to collide with
After processing all robots, any robot with health > 0 has survived. We then need to return these survivors in their original input order (not the sorted order we used for processing), which is why we track indices and filter the original health array at the end.
The beauty of this approach is that the stack naturally handles the collision chain reactions - when a strong left-moving robot defeats multiple right-moving robots in sequence, we simply keep popping from the stack until the collision resolution is complete.
Solution Approach
Let's walk through the implementation step by step:
1. Initialize Data Structures:
n = len(positions)
indices = list(range(n)) # [0, 1, 2, ..., n-1]
[stack](/problems/stack_intro) = []
We create an indices
list to track the original order of robots while allowing us to sort by position. The stack
will store indices of robots moving right.
2. Sort by Position:
indices.sort(key=lambda i: positions[i])
We sort the indices based on their corresponding positions. This ensures we process robots from left to right on the number line, which is crucial for correctly simulating collisions.
3. Process Each Robot:
for currentIndex in indices: if directions[currentIndex] == "R": [stack](/problems/stack_intro).append(currentIndex)
For each robot (in position order):
- If it's moving right ('R'), add its index to the stack. These robots are candidates for future collisions.
4. Handle Left-Moving Robots:
else: # directions[currentIndex] == "L" while [stack](/problems/stack_intro) and healths[currentIndex] > 0: topIndex = stack.pop()
When we encounter a left-moving robot, it needs to collide with right-moving robots (stored in the stack). We continue collisions while:
- There are right-moving robots in the stack
- The current left-moving robot is still alive (
healths[currentIndex] > 0
)
5. Resolve Individual Collisions:
if healths[topIndex] > healths[currentIndex]: healths[topIndex] -= 1 healths[currentIndex] = 0 [stack](/problems/stack_intro).append(topIndex) # Right robot survives, goes back to stack elif healths[topIndex] < healths[currentIndex]: healths[currentIndex] -= 1 healths[topIndex] = 0 # Left robot survives, continues to next collision else: # Equal health healths[currentIndex] = 0 healths[topIndex] = 0 # Both robots are eliminated
For each collision:
- Compare healths of the colliding robots
- Update healths based on collision rules (survivor loses 1 health, loser gets 0)
- If the right-moving robot survives, push it back to the stack
- If the left-moving robot survives, continue the while loop for more collisions
6. Collect Survivors:
result = [health for health in healths if health > 0] return result
After all collisions are resolved, we filter the original healths
array to get only surviving robots (health > 0). Since we modified the healths
array in place and iterate through it in original order, the survivors are automatically in the correct order as required.
Time Complexity: O(n log n)
for sorting, plus O(n)
for processing, giving O(n log n)
overall.
Space Complexity: O(n)
for the indices list and stack.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Input:
- positions = [3, 5, 2, 6]
- healths = [10, 10, 15, 10]
- directions = "RLRL"
Step 1: Setup and Sort We create indices [0, 1, 2, 3] and sort them by position:
- Sorted indices: [2, 0, 1, 3] (corresponding to positions [2, 3, 5, 6])
Step 2: Process robots from left to right by position
Processing robot at index 2 (position 2, health 15, direction 'R'):
- It's moving right, so add index 2 to stack
- Stack: [2]
- Current healths: [10, 10, 15, 10]
Processing robot at index 0 (position 3, health 10, direction 'R'):
- It's moving right, so add index 0 to stack
- Stack: [2, 0]
- Current healths: [10, 10, 15, 10]
Processing robot at index 1 (position 5, health 10, direction 'L'):
- It's moving left, so it collides with right-moving robots in the stack
- Pop index 0 from stack (robot at position 3, health 10)
- They have equal health (10 vs 10), so both are eliminated
- healths[0] = 0, healths[1] = 0
- Stack: [2]
- Current healths: [0, 0, 15, 10]
Processing robot at index 3 (position 6, health 10, direction 'L'):
- It's moving left, so it collides with right-moving robots in the stack
- Pop index 2 from stack (robot at position 2, health 15)
- Robot 2 has higher health (15 vs 10):
- healths[2] = 14 (survives with reduced health)
- healths[3] = 0 (eliminated)
- Push index 2 back to stack
- Stack: [2]
- Current healths: [0, 0, 14, 0]
Step 3: Collect survivors Filter the healths array for values > 0:
- healths[0] = 0 (eliminated)
- healths[1] = 0 (eliminated)
- healths[2] = 14 (survived)
- healths[3] = 0 (eliminated)
Final Result: [14]
The only survivor is the robot originally at index 2 (position 2), which survived with health 14 after defeating the robot at position 6.
Solution Implementation
1class Solution:
2 def survivedRobotsHealths(
3 self, positions: List[int], healths: List[int], directions: str
4 ) -> List[int]:
5 """
6 Simulates robot collisions on a line and returns surviving robots' healths.
7
8 Args:
9 positions: List of initial positions of robots
10 healths: List of initial health values of robots
11 directions: String where each character is 'L' or 'R' indicating direction
12
13 Returns:
14 List of health values for surviving robots in their original order
15 """
16 n = len(positions)
17 # Create indices to track original robot positions
18 indices = list(range(n))
19 # Stack to store indices of robots moving right
20 stack = []
21
22 # Sort indices by robot positions to process collisions from left to right
23 indices.sort(key=lambda i: positions[i])
24
25 # Process each robot in position order
26 for current_index in indices:
27 if directions[current_index] == "R":
28 # Robot moving right: add to stack for potential future collisions
29 stack.append(current_index)
30 else:
31 # Robot moving left: check for collisions with robots moving right
32 while stack and healths[current_index] > 0:
33 # Get the rightmost robot moving right
34 top_index = stack.pop()
35
36 if healths[top_index] > healths[current_index]:
37 # Right-moving robot wins: reduce its health by 1
38 healths[top_index] -= 1
39 healths[current_index] = 0
40 # Put winner back on stack
41 stack.append(top_index)
42 elif healths[top_index] < healths[current_index]:
43 # Left-moving robot wins: reduce its health by 1
44 healths[current_index] -= 1
45 healths[top_index] = 0
46 # Continue checking for more collisions
47 else:
48 # Both robots have equal health: both are destroyed
49 healths[current_index] = 0
50 healths[top_index] = 0
51
52 # Return health values of surviving robots (health > 0) in original order
53 result = [health for health in healths if health > 0]
54 return result
55
1class Solution {
2 public List<Integer> survivedRobotsHealths(int[] positions, int[] healths, String directions) {
3 int n = positions.length;
4
5 // Create an array of indices to track original positions after sorting
6 Integer[] indices = new Integer[n];
7 for (int i = 0; i < n; i++) {
8 indices[i] = i;
9 }
10
11 // Sort indices based on the actual positions of robots (left to right)
12 Arrays.sort(indices, (i, j) -> Integer.compare(positions[i], positions[j]));
13
14 // Stack to store indices of robots moving right (waiting for collision)
15 Stack<Integer> stack = new Stack<>();
16
17 // Process robots from left to right based on their positions
18 for (int currentIndex : indices) {
19 if (directions.charAt(currentIndex) == 'R') {
20 // Robot moving right: add to stack (potential collision candidate)
21 stack.push(currentIndex);
22 } else {
23 // Robot moving left: check for collisions with robots moving right
24 while (!stack.isEmpty() && healths[currentIndex] > 0) {
25 int rightMovingRobotIndex = stack.pop();
26
27 if (healths[rightMovingRobotIndex] > healths[currentIndex]) {
28 // Right-moving robot wins: decrease its health by 1
29 healths[rightMovingRobotIndex] -= 1;
30 healths[currentIndex] = 0;
31 stack.push(rightMovingRobotIndex); // Put winner back on stack
32 } else if (healths[rightMovingRobotIndex] < healths[currentIndex]) {
33 // Left-moving robot wins: decrease its health by 1
34 healths[currentIndex] -= 1;
35 healths[rightMovingRobotIndex] = 0;
36 // Continue checking for more collisions
37 } else {
38 // Both robots have equal health: both are destroyed
39 healths[currentIndex] = 0;
40 healths[rightMovingRobotIndex] = 0;
41 }
42 }
43 }
44 }
45
46 // Collect surviving robots' healths in their original order
47 List<Integer> result = new ArrayList<>();
48 for (int health : healths) {
49 if (health > 0) {
50 result.add(health);
51 }
52 }
53
54 return result;
55 }
56}
57
1class Solution {
2public:
3 vector<int> survivedRobotsHealths(vector<int>& positions, vector<int>& healths, string directions) {
4 int n = positions.size();
5
6 // Create an index array to track original positions after sorting
7 vector<int> indices(n);
8 iota(indices.begin(), indices.end(), 0);
9
10 // Stack to store indices of robots moving right
11 stack<int> rightMovingRobots;
12
13 // Sort indices based on robot positions (left to right on the line)
14 sort(indices.begin(), indices.end(), [&](int i, int j) {
15 return positions[i] < positions[j];
16 });
17
18 // Process robots from left to right based on their positions
19 for (int currentIndex : indices) {
20 if (directions[currentIndex] == 'R') {
21 // Robot moving right, add to stack for potential collision
22 rightMovingRobots.push(currentIndex);
23 } else {
24 // Robot moving left, check for collisions with right-moving robots
25 while (!rightMovingRobots.empty() && healths[currentIndex] > 0) {
26 int rightRobotIndex = rightMovingRobots.top();
27 rightMovingRobots.pop();
28
29 if (healths[rightRobotIndex] > healths[currentIndex]) {
30 // Right-moving robot survives with reduced health
31 healths[rightRobotIndex] -= 1;
32 healths[currentIndex] = 0;
33 rightMovingRobots.push(rightRobotIndex);
34 } else if (healths[rightRobotIndex] < healths[currentIndex]) {
35 // Left-moving robot survives with reduced health
36 healths[currentIndex] -= 1;
37 healths[rightRobotIndex] = 0;
38 } else {
39 // Both robots have equal health and destroy each other
40 healths[currentIndex] = 0;
41 healths[rightRobotIndex] = 0;
42 }
43 }
44 }
45 }
46
47 // Collect surviving robots' healths in original order
48 vector<int> result;
49 for (int i = 0; i < n; ++i) {
50 if (healths[i] > 0) {
51 result.push_back(healths[i]);
52 }
53 }
54
55 return result;
56 }
57};
58
1/**
2 * Simulates robot collisions and returns the remaining healths after all collisions
3 * @param positions - Array of robot positions on a line
4 * @param healths - Array of robot health values
5 * @param directions - String where each character is 'L' or 'R' indicating robot direction
6 * @returns Array of remaining healths in original order (0 for destroyed robots)
7 */
8function survivedRobotsHealths(
9 positions: number[],
10 healths: number[],
11 directions: string,
12): number[] {
13 // Create array of indices to track original positions
14 const indices = Array.from({ length: positions.length }, (_, index) => index);
15
16 // Stack to track robots that haven't collided yet
17 const robotStack: number[] = [];
18
19 // Sort indices by robot positions (left to right)
20 indices.sort((indexA, indexB) => positions[indexA] - positions[indexB]);
21
22 // Process each robot from left to right
23 for (let currentRobotIndex of indices) {
24 // Check for collisions with robots in the stack
25 while (robotStack.length > 0) {
26 const topStackIndex = robotStack[robotStack.length - 1];
27
28 // Check if robots are moving towards each other (R meets L)
29 const isColliding = directions[topStackIndex] === 'R' && directions[currentRobotIndex] === 'L';
30 if (!isColliding) {
31 break;
32 }
33
34 // Handle collision based on health values
35 if (healths[topStackIndex] === healths[currentRobotIndex]) {
36 // Both robots destroy each other
37 healths[topStackIndex] = -1;
38 healths[currentRobotIndex] = -1;
39 currentRobotIndex = -1;
40 robotStack.pop();
41 break;
42 } else if (healths[topStackIndex] < healths[currentRobotIndex]) {
43 // Left robot destroyed, right robot survives with reduced health
44 healths[topStackIndex] = -1;
45 healths[currentRobotIndex]--;
46 robotStack.pop();
47 } else {
48 // Right robot destroyed, left robot survives with reduced health
49 healths[currentRobotIndex] = -1;
50 currentRobotIndex = -1;
51 healths[topStackIndex]--;
52 break;
53 }
54 }
55
56 // Add surviving robot to stack
57 if (currentRobotIndex !== -1) {
58 robotStack.push(currentRobotIndex);
59 }
60 }
61
62 // Filter out destroyed robots (health = -1) and return remaining healths
63 return healths.filter(health => health !== -1);
64}
65
Time and Space Complexity
Time Complexity: O(n log n)
The time complexity is dominated by the sorting operation:
- Creating the indices list takes
O(n)
time - Sorting the indices based on positions takes
O(n log n)
time - The main loop iterates through all
n
indices once:O(n)
- For each robot moving left, the while loop processes collisions with robots moving right
- Each robot can be pushed to and popped from the stack at most once
- Therefore, the total work done by all while loop iterations across all indices is
O(n)
- Creating the final result list by filtering healths takes
O(n)
time
Overall: O(n) + O(n log n) + O(n) + O(n) = O(n log n)
Space Complexity: O(n)
The space complexity consists of:
indices
list storingn
indices:O(n)
stack
can contain at mostn
elements (all robots moving right):O(n)
result
list can contain at mostn
elements:O(n)
Overall: O(n) + O(n) + O(n) = O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Not Preserving Original Order in the Result
The Pitfall: A common mistake is returning the surviving robots in the order they appear on the line (sorted by position) rather than in their original input order. For example:
# WRONG: Returns survivors in position-sorted order
def survivedRobotsHealths(positions, healths, directions):
robots = sorted(zip(positions, healths, directions))
# ... process collisions ...
return [h for p, h, d in robots if h > 0] # Wrong order!
This would fail for input like positions=[5,4,3]
, healths=[2,17,9]
, directions="RLR"
where the survivor at position 4 should be returned as [17]
(second in original order), not based on its position.
The Solution: Track the original indices throughout the process and modify the health array in-place:
# CORRECT: Maintains original order
indices = list(range(n))
indices.sort(key=lambda i: positions[i]) # Sort indices, not robots
# Process using indices...
healths[index] = new_health # Modify original array
return [h for h in healths if h > 0] # Preserves original order
2. Incorrectly Handling the Collision Stack
The Pitfall: Forgetting to put the right-moving robot back on the stack when it survives a collision:
# WRONG: Doesn't return survivor to stack if healths[top_index] > healths[current_index]: healths[top_index] -= 1 healths[current_index] = 0 # Missing: stack.append(top_index)
This causes the surviving right-moving robot to disappear from future collision checks, leading to incorrect results.
The Solution: Always return the surviving right-moving robot to the stack:
# CORRECT: Returns survivor to stack if healths[top_index] > healths[current_index]: healths[top_index] -= 1 healths[current_index] = 0 stack.append(top_index) # Critical line!
3. Processing Robots in Input Order Instead of Position Order
The Pitfall: Processing robots in the order they appear in the input arrays rather than by their positions on the line:
# WRONG: Processes in input order
for i in range(n):
if directions[i] == "R":
stack.append(i)
else:
# Process collisions...
This fails because collisions happen based on physical position, not input order. A robot at position 10 moving left should collide with a robot at position 8 moving right, regardless of their order in the input.
The Solution: Sort the processing order by position while maintaining index references:
# CORRECT: Process by position order
indices = list(range(n))
indices.sort(key=lambda i: positions[i])
for current_index in indices:
# Now processing in correct spatial order
4. Not Handling Edge Cases Properly
The Pitfall: Not considering cases where:
- All robots move in the same direction (no collisions)
- No robots survive (should return empty array, not array of zeros)
- Single robot (no possible collisions)
The Solution: The algorithm naturally handles these cases if implemented correctly:
- Same direction: Stack either stays empty (all 'L') or accumulates all robots (all 'R')
- No survivors: The list comprehension
[h for h in healths if h > 0]
returns empty array - Single robot: Loop executes once, no collisions possible
The key is using if health > 0
when collecting results rather than checking for specific values.
Which of the following array represent a max heap?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!