657. Robot Return to Origin
Problem Description
You have a robot that starts at position (0, 0)
on a 2D plane. The robot can move in four directions based on commands given as a string:
'U'
moves the robot up (increases y-coordinate by 1)'D'
moves the robot down (decreases y-coordinate by 1)'L'
moves the robot left (decreases x-coordinate by 1)'R'
moves the robot right (increases x-coordinate by 1)
Given a string moves
containing a sequence of these movement commands, determine if the robot returns to its starting position (0, 0)
after executing all the moves.
For example:
- If
moves = "UD"
, the robot moves up then down, returning to(0, 0)
, so returntrue
- If
moves = "LL"
, the robot moves left twice, ending at(-2, 0)
, so returnfalse
The solution tracks the robot's position using coordinates (x, y)
. For each move command in the string, it updates the coordinates accordingly. After processing all moves, if both x
and y
equal 0
, the robot has returned to the origin.
The key insight is that for the robot to return to the origin, the total displacement in both the horizontal and vertical directions must be zero. This means the number of 'L'
moves must equal the number of 'R'
moves, and the number of 'U'
moves must equal the number of 'D'
moves.
Intuition
Think about what it means for the robot to return to its starting position. If the robot ends up where it started, then its total displacement must be zero in both dimensions.
Consider the robot's movement on a coordinate plane. Each move changes the robot's position:
- Moving right increases x-coordinate, moving left decreases it
- Moving up increases y-coordinate, moving down decreases it
For the robot to return to (0, 0)
, whatever distance it traveled in one direction must be canceled out by traveling the same distance in the opposite direction. This means:
- Every
'R'
move (x + 1) needs to be balanced by an'L'
move (x - 1) - Every
'U'
move (y + 1) needs to be balanced by a'D'
move (y - 1)
Instead of counting occurrences of each character, we can simulate the actual movement. Start at (0, 0)
and track the position as we process each move. This is more intuitive because we're directly modeling what the robot does - we move step by step and see where we end up.
The beauty of this approach is its simplicity: we don't need to think about complex patterns or optimize anything. We just follow the robot's path and check if it comes back home. If after all moves x = 0
and y = 0
, then the robot made a complete circle back to the origin.
Solution Approach
We maintain a coordinate (x, y)
to represent the robot's position on the 2D plane, starting at (0, 0)
.
The implementation follows these steps:
-
Initialize coordinates: Set
x = 0
andy = 0
to represent the starting position at the origin. -
Process each move: Traverse through each character
c
in themoves
string and update the coordinates based on the movement direction:- If
c
is'U'
: incrementy
by 1 (move up) - If
c
is'D'
: decrementy
by 1 (move down) - If
c
is'L'
: decrementx
by 1 (move left) - If
c
is'R'
: incrementx
by 1 (move right)
- If
-
Check final position: After processing all moves, check if both
x == 0
andy == 0
. If true, the robot has returned to the origin.
The solution uses a match
statement (Python 3.10+) for clean branching based on the move character. This could alternatively be implemented using if-elif statements or a dictionary mapping.
The time complexity is O(n)
where n
is the length of the moves string, as we traverse it once. The space complexity is O(1)
since we only use two variables to track the position regardless of input size.
The algorithm essentially simulates the robot's movement in real-time, tracking its displacement from the origin. If the final displacement is (0, 0)
, the robot has made a complete circle back to where it started.
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 the solution with moves = "UDLR"
:
Step 1: Initialize position
- Start at origin:
x = 0
,y = 0
- Current position:
(0, 0)
Step 2: Process each move
Move 1: 'U'
(Up)
- Update:
y = y + 1 = 0 + 1 = 1
- Current position:
(0, 1)
Move 2: 'D'
(Down)
- Update:
y = y - 1 = 1 - 1 = 0
- Current position:
(0, 0)
Move 3: 'L'
(Left)
- Update:
x = x - 1 = 0 - 1 = -1
- Current position:
(-1, 0)
Move 4: 'R'
(Right)
- Update:
x = x + 1 = -1 + 1 = 0
- Current position:
(0, 0)
Step 3: Check final position
- Final coordinates:
x = 0
,y = 0
- Since both
x
andy
equal 0, the robot returned to origin - Return:
true
Let's trace another example where the robot doesn't return: moves = "LLU"
:
Step 1: Initialize
- Start:
x = 0
,y = 0
Step 2: Process moves
- Move 1
'L'
:x = -1
, position:(-1, 0)
- Move 2
'L'
:x = -2
, position:(-2, 0)
- Move 3
'U'
:y = 1
, position:(-2, 1)
Step 3: Check final position
- Final:
x = -2
,y = 1
- Not at origin (0, 0)
- Return:
false
The key insight: each move affects exactly one coordinate by exactly 1 unit. For the robot to return home, all movements must cancel out perfectly.
Solution Implementation
1class Solution:
2 def judgeCircle(self, moves: str) -> bool:
3 """
4 Determines if a sequence of moves returns to the origin (0, 0).
5
6 Args:
7 moves: A string containing move commands ('U', 'D', 'L', 'R')
8
9 Returns:
10 True if the final position is (0, 0), False otherwise
11 """
12 # Initialize coordinates at origin
13 x_position = 0
14 y_position = 0
15
16 # Process each move command
17 for move in moves:
18 if move == "U": # Move up
19 y_position += 1
20 elif move == "D": # Move down
21 y_position -= 1
22 elif move == "L": # Move left
23 x_position -= 1
24 elif move == "R": # Move right
25 x_position += 1
26
27 # Check if we returned to origin
28 return x_position == 0 and y_position == 0
29
1class Solution {
2 /**
3 * Determines if a sequence of moves returns to the origin position.
4 * Starting from origin (0, 0), each move changes the position:
5 * 'U' (Up): moves up by increasing y-coordinate
6 * 'D' (Down): moves down by decreasing y-coordinate
7 * 'L' (Left): moves left by decreasing x-coordinate
8 * 'R' (Right): moves right by increasing x-coordinate
9 *
10 * @param moves String containing sequence of moves (U, D, L, R)
11 * @return true if the final position is origin (0, 0), false otherwise
12 */
13 public boolean judgeCircle(String moves) {
14 // Initialize coordinates at origin
15 int xCoordinate = 0;
16 int yCoordinate = 0;
17
18 // Process each move character
19 for (char move : moves.toCharArray()) {
20 switch (move) {
21 case 'U': // Move up
22 yCoordinate++;
23 break;
24 case 'D': // Move down
25 yCoordinate--;
26 break;
27 case 'L': // Move left
28 xCoordinate--;
29 break;
30 case 'R': // Move right
31 xCoordinate++;
32 break;
33 default:
34 // Invalid move character, ignore
35 break;
36 }
37 }
38
39 // Check if returned to origin
40 return xCoordinate == 0 && yCoordinate == 0;
41 }
42}
43
1class Solution {
2public:
3 /**
4 * Determines if a robot returns to the origin after executing all moves.
5 * The robot starts at position (0, 0) on a 2D plane.
6 *
7 * @param moves A string containing move commands:
8 * 'U' - move up (increase y-coordinate)
9 * 'D' - move down (decrease y-coordinate)
10 * 'L' - move left (decrease x-coordinate)
11 * 'R' - move right (increase x-coordinate)
12 * @return true if the robot returns to origin (0, 0), false otherwise
13 */
14 bool judgeCircle(string moves) {
15 // Initialize coordinates to track robot's position
16 int xCoordinate = 0;
17 int yCoordinate = 0;
18
19 // Process each move command
20 for (char move : moves) {
21 switch (move) {
22 case 'U': // Move up
23 yCoordinate++;
24 break;
25 case 'D': // Move down
26 yCoordinate--;
27 break;
28 case 'L': // Move left
29 xCoordinate--;
30 break;
31 case 'R': // Move right
32 xCoordinate++;
33 break;
34 }
35 }
36
37 // Check if robot returned to origin
38 return (xCoordinate == 0 && yCoordinate == 0);
39 }
40};
41
1/**
2 * Determines if a sequence of moves returns to the origin point (0, 0)
3 * @param moves - String containing move commands: 'U' (up), 'D' (down), 'L' (left), 'R' (right)
4 * @returns true if the moves return to origin, false otherwise
5 */
6function judgeCircle(moves: string): boolean {
7 // Initialize coordinates at origin
8 let xCoordinate: number = 0;
9 let yCoordinate: number = 0;
10
11 // Process each move command
12 for (const move of moves) {
13 switch (move) {
14 case 'U':
15 // Move up: increase y-coordinate
16 yCoordinate++;
17 break;
18 case 'D':
19 // Move down: decrease y-coordinate
20 yCoordinate--;
21 break;
22 case 'L':
23 // Move left: decrease x-coordinate
24 xCoordinate--;
25 break;
26 case 'R':
27 // Move right: increase x-coordinate
28 xCoordinate++;
29 break;
30 }
31 }
32
33 // Check if we returned to the origin
34 return xCoordinate === 0 && yCoordinate === 0;
35}
36
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the string moves
. The algorithm iterates through each character in the input string exactly once using a single for loop. Each operation inside the loop (the match statement and the increment/decrement operations) takes constant time O(1)
. Therefore, the total time complexity is O(n) * O(1) = O(n)
.
Space Complexity: O(1)
. The algorithm uses only two integer variables x
and y
to track the position, regardless of the input size. The space usage remains constant and does not grow with the length of the input string. The loop variable c
also uses constant space. Since no additional data structures are created that scale with the input, the space complexity is O(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Inefficient String Operations in Other Approaches
A common pitfall is attempting to optimize by counting characters using multiple passes through the string or using string methods like count()
:
# Inefficient approach - multiple passes
def judgeCircle(moves: str) -> bool:
return moves.count('U') == moves.count('D') and \
moves.count('L') == moves.count('R')
While this works correctly and is more concise, it makes 4 passes through the string (one for each count()
call), resulting in O(4n)
operations compared to the single-pass O(n)
simulation approach.
2. Using Complex Data Structures Unnecessarily
Some might overcomplicate by using dictionaries or collections.Counter:
# Overly complex for this problem
from collections import Counter
def judgeCircle(moves: str) -> bool:
counts = Counter(moves)
return counts['U'] == counts['D'] and counts['L'] == counts['R']
This adds unnecessary overhead for a simple problem where two integer variables suffice.
3. Incorrect Coordinate System Understanding
A subtle pitfall is mixing up which direction corresponds to positive/negative changes:
# Wrong direction mapping for move in moves: if move == "U": y_position -= 1 # Wrong! Should be += 1 elif move == "D": y_position += 1 # Wrong! Should be -= 1
4. Not Handling Edge Cases
Forgetting to consider edge cases like:
- Empty string (should return
True
as robot hasn't moved) - Single character (will always return
False
unless empty)
Solution to avoid these pitfalls:
- Stick with the simple simulation approach for clarity and single-pass efficiency
- Use consistent coordinate conventions (up = +y, right = +x)
- Consider using a direction dictionary for cleaner code if preferred:
def judgeCircle(moves: str) -> bool:
x, y = 0, 0
directions = {'U': (0, 1), 'D': (0, -1), 'L': (-1, 0), 'R': (1, 0)}
for move in moves:
if move in directions:
dx, dy = directions[move]
x += dx
y += dy
return x == 0 and y == 0
Which of the following uses divide and conquer strategy?
Recommended Readings
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
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!