1041. Robot Bounded In Circle
Problem Description
You have a robot on an infinite 2D plane that starts at position (0, 0)
facing north. The coordinate system is defined as:
- North: positive y-axis direction
- South: negative y-axis direction
- East: positive x-axis direction
- West: negative x-axis direction
The robot can execute three types of instructions:
"G"
: Move forward 1 unit in the current direction"L"
: Turn 90 degrees left (counter-clockwise)"R"
: Turn 90 degrees right (clockwise)
Given a string instructions
containing these commands, the robot will execute the entire instruction sequence and then repeat it infinitely.
Your task is to determine if the robot will stay within a bounded circle on the plane. Return true
if the robot's path is bounded (never leaves some circle), and false
otherwise.
For example:
- If
instructions = "GGLLGG"
, the robot moves forward 2 units, turns left twice (facing south), then moves forward 2 units. After one cycle, it returns to(0, 0)
facing south. Since it returns to origin, it will stay bounded. - If
instructions = "GG"
, the robot keeps moving north indefinitely, so it's not bounded.
The key insight is that after executing the instruction sequence once, if either:
- The robot returns to the origin
(0, 0)
, OR - The robot faces a different direction than north
Then the robot will stay within a bounded circle. This is because changing direction means the robot will eventually trace a closed path after multiple cycles (at most 4 cycles for a full rotation back to north).
Intuition
The key insight comes from observing what happens when we repeat the instruction sequence multiple times. Let's think about what determines whether the robot stays bounded:
After executing the instructions once, the robot ends up at some position (x, y)
facing some direction. Now we need to understand when repeating this pattern keeps the robot bounded.
Case 1: Robot returns to origin after one cycle
If after one execution the robot is back at (0, 0)
, then no matter which direction it faces, repeating the instructions will just keep bringing it back to origin. The robot traces a closed loop and stays bounded.
Case 2: Robot doesn't return to origin
This is more interesting. The robot is at position (x, y)
after one cycle. What happens next depends on which direction it's facing:
-
Still facing North (same as start): The second execution will move the robot by another
(x, y)
, third by another(x, y)
, and so on. The position aftern
cycles becomes(n*x, n*y)
. Sincex
ory
is non-zero (not at origin), the robot keeps moving away infinitely - unbounded. -
Facing West (turned left once): After the second cycle, the displacement is
(-y, x)
relative to first cycle's end. After the third cycle, it's(-x, -y)
. After the fourth, it's(y, -x)
. Adding these up:(x, y) + (-y, x) + (-x, -y) + (y, -x) = (0, 0)
. The robot returns to origin after 4 cycles - bounded. -
Facing South (turned 180°): The second cycle moves the robot by
(-x, -y)
. Combined with the first cycle's(x, y)
, we get back to(0, 0)
after just 2 cycles - bounded. -
Facing East (turned right once): Similar to facing west, after 4 cycles the displacements cancel out and return to origin - bounded.
The pattern emerges: if the robot changes direction after one cycle, the different orientations cause the displacement vectors to eventually cancel out (after at most 4 cycles). But if it maintains the original direction, it keeps accumulating displacement in the same direction forever.
Therefore, we can determine boundedness by checking after one execution:
- Did we return to origin? (distances cancel:
dist[North] == dist[South]
anddist[East] == dist[West]
) - OR did we change direction? (
k != 0
wherek
represents the final direction)
If either condition is true, the robot stays bounded.
Learn more about Math patterns.
Solution Approach
The implementation uses a simulation approach to track the robot's movement through one complete execution of the instruction sequence.
Data Structures:
k
: An integer representing the robot's current direction (0=North, 1=West, 2=South, 3=East)dist
: An array of size 4 to track the distance traveled in each direction
Algorithm Steps:
-
Initialize the tracking variables:
- Set
k = 0
(robot starts facing North) - Create
dist = [0, 0, 0, 0]
to track distances in each direction
- Set
-
Process each instruction: For each character
c
in the instruction string:- Left turn (
'L'
): Update direction byk = (k + 1) % 4
- This rotates counter-clockwise through: North→West→South→East→North
- Right turn (
'R'
): Update direction byk = (k + 3) % 4
- Adding 3 is equivalent to subtracting 1 in modulo 4, rotating clockwise
- This rotates through: North→East→South→West→North
- Go forward (
'G'
): Increment the distance in current direction bydist[k] += 1
- Left turn (
-
Check boundedness conditions: After processing all instructions, return
true
if either:- Robot returns to origin:
dist[0] == dist[2] and dist[1] == dist[3]
dist[0]
(North) equalsdist[2]
(South): net vertical displacement is 0dist[1]
(West) equalsdist[3]
(East): net horizontal displacement is 0
- OR robot changed direction:
k != 0
- The robot is no longer facing North after one cycle
- Robot returns to origin:
Why this works:
- If the robot moves North
n
times and Southn
times, the vertical movements cancel out - If the robot moves East
m
times and Westm
times, the horizontal movements cancel out - Together, these conditions check if the robot returns to
(0, 0)
- If the robot doesn't return to origin but changed direction (
k != 0
), we know from our analysis that after at most 4 cycles, the displacement vectors will cancel out due to rotation
The elegance of this solution is that we don't need to simulate multiple cycles - just one execution tells us everything we need to determine boundedness.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the example instructions = "GGLLGG"
to see how our solution determines boundedness.
Initial State:
- Direction
k = 0
(facing North) - Distance array
dist = [0, 0, 0, 0]
representing[North, West, South, East]
Processing each instruction:
-
'G' (1st): Move forward while facing North
dist[0] += 1
→dist = [1, 0, 0, 0]
- Robot position:
(0, 1)
-
'G' (2nd): Move forward while still facing North
dist[0] += 1
→dist = [2, 0, 0, 0]
- Robot position:
(0, 2)
-
'L' (3rd): Turn left
k = (0 + 1) % 4 = 1
(now facing West)- No position change
-
'L' (4th): Turn left again
k = (1 + 1) % 4 = 2
(now facing South)- No position change
-
'G' (5th): Move forward while facing South
dist[2] += 1
→dist = [2, 0, 1, 0]
- Robot position:
(0, 1)
-
'G' (6th): Move forward while still facing South
dist[2] += 1
→dist = [2, 0, 2, 0]
- Robot position:
(0, 0)
Final Check:
- Final direction:
k = 2
(facing South, not North) - Final distances:
dist = [2, 0, 2, 0]
Boundedness conditions:
-
Check if at origin:
dist[0] == dist[2]
? →2 == 2
✓ (North and South distances equal)dist[1] == dist[3]
? →0 == 0
✓ (West and East distances equal)- Robot returned to origin!
-
Check if direction changed:
k != 0
? →2 != 0
✓ (Also true - facing South, not North)
Since the robot returns to origin (condition 1 is satisfied), the function returns true
. The robot will stay bounded, repeatedly tracing the same rectangular path: up 2, turn around, down 2, and back to start.
Solution Implementation
1class Solution:
2 def isRobotBounded(self, instructions: str) -> bool:
3 # Direction index: 0=North, 1=West, 2=South, 3=East
4 direction = 0
5
6 # Track distance moved in each direction
7 distance_per_direction = [0] * 4
8
9 # Process each instruction
10 for instruction in instructions:
11 if instruction == 'L':
12 # Turn left: rotate 90 degrees counter-clockwise
13 direction = (direction + 1) % 4
14 elif instruction == 'R':
15 # Turn right: rotate 90 degrees clockwise
16 # Adding 3 is equivalent to subtracting 1 with modulo 4
17 direction = (direction + 3) % 4
18 else: # instruction == 'G'
19 # Move forward in current direction
20 distance_per_direction[direction] += 1
21
22 # Robot forms a circle if:
23 # 1. It returns to origin (North-South distances equal AND East-West distances equal)
24 # 2. OR it's not facing North after one cycle (will eventually circle back)
25 is_at_origin = (distance_per_direction[0] == distance_per_direction[2] and
26 distance_per_direction[1] == distance_per_direction[3])
27 is_not_facing_north = direction != 0
28
29 return is_at_origin or is_not_facing_north
30
1class Solution {
2 public boolean isRobotBounded(String instructions) {
3 // Direction index: 0=North, 1=West, 2=South, 3=East
4 int direction = 0;
5
6 // Array to track distance moved in each direction
7 // distanceInDirection[0] = North, distanceInDirection[1] = West
8 // distanceInDirection[2] = South, distanceInDirection[3] = East
9 int[] distanceInDirection = new int[4];
10
11 // Process each instruction in the sequence
12 for (int i = 0; i < instructions.length(); i++) {
13 char instruction = instructions.charAt(i);
14
15 if (instruction == 'L') {
16 // Turn left: rotate 90 degrees counter-clockwise
17 // North(0) -> West(1) -> South(2) -> East(3) -> North(0)
18 direction = (direction + 1) % 4;
19 } else if (instruction == 'R') {
20 // Turn right: rotate 90 degrees clockwise
21 // North(0) -> East(3) -> South(2) -> West(1) -> North(0)
22 // Adding 3 is equivalent to subtracting 1 in modulo 4
23 direction = (direction + 3) % 4;
24 } else {
25 // instruction == 'G': move forward one unit in current direction
26 distanceInDirection[direction]++;
27 }
28 }
29
30 // Robot forms a circle if:
31 // 1. Net displacement is zero (North steps = South steps AND West steps = East steps)
32 // 2. Robot is not facing North after instructions (will eventually return to origin)
33 return (distanceInDirection[0] == distanceInDirection[2] &&
34 distanceInDirection[1] == distanceInDirection[3]) ||
35 (direction != 0);
36 }
37}
38
1class Solution {
2public:
3 bool isRobotBounded(string instructions) {
4 // Track distance moved in each of the 4 directions (North, West, South, East)
5 int distance[4] = {0, 0, 0, 0};
6
7 // Current direction: 0=North, 1=West, 2=South, 3=East
8 int currentDirection = 0;
9
10 // Process each instruction
11 for (char& instruction : instructions) {
12 if (instruction == 'L') {
13 // Turn left: rotate 90 degrees counterclockwise
14 currentDirection = (currentDirection + 1) % 4;
15 } else if (instruction == 'R') {
16 // Turn right: rotate 90 degrees clockwise
17 // Adding 3 is equivalent to subtracting 1 in modulo 4
18 currentDirection = (currentDirection + 3) % 4;
19 } else {
20 // Move forward ('G'): increment distance in current direction
21 ++distance[currentDirection];
22 }
23 }
24
25 // Robot forms a circle if:
26 // 1. It returns to origin (North-South distances cancel, East-West distances cancel), OR
27 // 2. It's not facing North after one cycle (will eventually return after 2 or 4 cycles)
28 return (distance[0] == distance[2] && distance[1] == distance[3]) || currentDirection != 0;
29 }
30};
31
1/**
2 * Determines if a robot returns to origin or stays in a bounded circle
3 * after executing the given instructions infinitely.
4 *
5 * @param instructions - String containing 'G' (go straight), 'L' (turn left), 'R' (turn right)
6 * @returns true if robot is bounded in a circle, false otherwise
7 */
8function isRobotBounded(instructions: string): boolean {
9 // Track distance moved in each direction: North, East, South, West
10 // Index 0: North, 1: East, 2: South, 3: West
11 const distanceInEachDirection: number[] = new Array(4).fill(0);
12
13 // Current direction the robot is facing (0: North, 1: East, 2: South, 3: West)
14 let currentDirection: number = 0;
15
16 // Process each instruction
17 for (const instruction of instructions) {
18 if (instruction === 'L') {
19 // Turn left: rotate 90 degrees counter-clockwise
20 currentDirection = (currentDirection + 1) % 4;
21 } else if (instruction === 'R') {
22 // Turn right: rotate 90 degrees clockwise
23 // Adding 3 is equivalent to subtracting 1 in modulo 4
24 currentDirection = (currentDirection + 3) % 4;
25 } else {
26 // Move forward ('G'): increment distance in current direction
27 distanceInEachDirection[currentDirection]++;
28 }
29 }
30
31 // Robot is bounded if:
32 // 1. It returns to origin (net displacement is zero in both axes)
33 // North-South displacement: distanceInEachDirection[0] - distanceInEachDirection[2] = 0
34 // East-West displacement: distanceInEachDirection[1] - distanceInEachDirection[3] = 0
35 // 2. OR it doesn't face north after one cycle (will eventually return after 2 or 4 cycles)
36 return (distanceInEachDirection[0] === distanceInEachDirection[2] &&
37 distanceInEachDirection[1] === distanceInEachDirection[3]) ||
38 currentDirection !== 0;
39}
40
Time and Space Complexity
Time Complexity: O(n)
The algorithm iterates through the instructions string exactly once. For each character in the string, it performs constant-time operations:
- Checking the character type (
'L'
,'R'
, or'G'
) - Updating the direction
k
with modulo operations - Incrementing a distance counter
Since we process each of the n
characters exactly once with O(1)
operations per character, the overall time complexity is O(n)
, where n
is the length of the instruction string.
Space Complexity: O(1)
The algorithm uses a fixed amount of extra space:
- Variable
k
to track the current direction (single integer) - Array
dist
of fixed size 4 to track distances in each direction
The space usage does not depend on the input size n
. The dist
array always has exactly 4 elements regardless of how long the instructions string is. Therefore, the space complexity is O(1)
(constant space).
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Direction Mapping Leading to Wrong Distance Tracking
One of the most common mistakes is using an inconsistent or incorrect mapping between direction indices and actual compass directions. This leads to recording movements in the wrong direction slots.
Pitfall Example:
# Incorrect mapping where developers might use: # 0=North, 1=East, 2=South, 3=West (clockwise order) # But then use the wrong rotation logic direction = 0 # North distance = [0] * 4 for instruction in instructions: if instruction == 'L': direction = (direction + 1) % 4 # This would go North->East (wrong!) elif instruction == 'R': direction = (direction - 1) % 4 # This would go North->West (wrong!) else: distance[direction] += 1
Why it's wrong: The rotation logic doesn't match the direction mapping. If you use clockwise ordering (N→E→S→W), then turning left should decrease the index, not increase it.
Solution: Stick to one consistent mapping and ensure your rotation logic matches:
Option 1 - Counter-clockwise mapping (as in the original solution):
# 0=North, 1=West, 2=South, 3=East # Left turn: (direction + 1) % 4 # Right turn: (direction + 3) % 4
Option 2 - Clockwise mapping:
# 0=North, 1=East, 2=South, 3=West # Left turn: (direction + 3) % 4 # or (direction - 1 + 4) % 4 # Right turn: (direction + 1) % 4
2. Using Coordinate Tracking Instead of Direction-Based Distance
Another pitfall is trying to track the exact (x, y) position and getting confused with direction changes.
Pitfall Example:
def isRobotBounded(self, instructions: str) -> bool:
x, y = 0, 0
dx, dy = 0, 1 # Initially facing north
for instruction in instructions:
if instruction == 'L':
# Incorrect rotation transformation
dx, dy = dy, dx # Wrong! This doesn't rotate 90 degrees
elif instruction == 'R':
dx, dy = -dy, dx # May have signs wrong
else:
x += dx
y += dy
return (x == 0 and y == 0) or (dx != 0 or dy != 1)
Why it's wrong: The rotation transformations for (dx, dy) are easy to get wrong. The correct 90-degree rotations are:
- Left turn:
(dx, dy) -> (-dy, dx)
- Right turn:
(dx, dy) -> (dy, -dx)
Solution: If using coordinate approach, ensure correct rotation matrices:
def isRobotBounded(self, instructions: str) -> bool:
x, y = 0, 0
dx, dy = 0, 1 # Initially facing north
for instruction in instructions:
if instruction == 'L':
dx, dy = -dy, dx # Correct 90° counter-clockwise
elif instruction == 'R':
dx, dy = dy, -dx # Correct 90° clockwise
else:
x += dx
y += dy
return (x == 0 and y == 0) or (dx != 0 or dy != 1)
3. Forgetting to Check Both Conditions for Boundedness
Pitfall Example:
def isRobotBounded(self, instructions: str) -> bool:
# ... simulation code ...
# Only checking if robot returns to origin
return distance[0] == distance[2] and distance[1] == distance[3]
# Forgot to check if direction changed!
Why it's wrong: The robot can be bounded even if it doesn't return to origin after one cycle, as long as it's facing a different direction. After at most 4 cycles, it will form a closed loop.
Solution: Always check both conditions:
return (distance[0] == distance[2] and distance[1] == distance[3]) or direction != 0
Which of these pictures shows the visit order of a depth-first search?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!