Facebook Pixel

754. Reach a Number

Problem Description

You start at position 0 on an infinite number line and need to reach a target position.

You can make moves following these rules:

  • Each move allows you to go either left or right
  • On the 1st move, you take 1 step in your chosen direction
  • On the 2nd move, you take 2 steps in your chosen direction
  • On the 3rd move, you take 3 steps in your chosen direction
  • And so on... on the i-th move, you take i steps

Your goal is to find the minimum number of moves needed to reach exactly the target position.

For example, if target = 2:

  • Move 1: Go right 1 step (position = 1)
  • Move 2: Go right 2 steps (position = 3)
  • Move 3: Go left 3 steps (position = 0)
  • Move 4: Go right 4 steps (position = 4)
  • Move 5: Go left 5 steps (position = -1)
  • Move 6: Go right 6 steps (position = 5)
  • Move 7: Go left 7 steps (position = -2)
  • Move 8: Go right 8 steps (position = 6)
  • Move 9: Go left 9 steps (position = -3)
  • Move 10: Go right 10 steps (position = 7)
  • Move 11: Go left 11 steps (position = -4)

Actually, a better sequence would be:

  • Move 1: Go right 1 step (position = 1)
  • Move 2: Go left 2 steps (position = -1)
  • Move 3: Go right 3 steps (position = 2)

So the answer would be 3 moves.

The solution leverages the fact that due to symmetry, we can work with the absolute value of the target. The key insight is that we keep moving in the positive direction (accumulating sum 1 + 2 + 3 + ... + k) until we either hit the target exactly or overshoot it. If we overshoot by an even amount, we can flip one of our previous moves from positive to negative to reach the target exactly. The move we flip would be the one with value equal to half the overshoot amount.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about what happens when we make moves. If we always move in the positive direction, after k moves, we'd be at position 1 + 2 + 3 + ... + k = k*(k+1)/2.

Since we can move left or right at each step, we can think of each move as having a sign: positive for right, negative for left. So our final position is actually the sum of moves with their signs: ±1 ± 2 ± 3 ± ... ± k.

Here's the key observation: if we're at position s after always moving right, and s > target, then we've overshot by s - target. If this overshoot is even, we can reach the target exactly! How?

Consider that we made all positive moves to reach s. If we flip one of those moves from positive to negative, we lose twice that move's value from our sum. For example, if we flip move i from +i to -i, our sum decreases by 2i.

So if s - target = 2i for some move i we already made, we can flip that specific move to negative and reach the target exactly. This is why we need (s - target) % 2 == 0.

What if the overshoot is odd? Then we can't fix it by flipping any single existing move (since flipping changes the sum by an even amount). So we keep adding more moves until the overshoot becomes even.

This explains the algorithm: keep moving in the positive direction (adding 1, 2, 3, ...) until:

  1. We reach or pass the target, AND
  2. The overshoot (s - target) is even

When both conditions are met, the number of moves we've made is our answer. The actual sequence would involve flipping the appropriate move(s) to negative, but we don't need to track which ones - we just need to count the total moves.

Learn more about Math and Binary Search patterns.

Solution Approach

Based on our mathematical analysis, we implement the solution with a simple loop:

  1. Handle symmetry: First, we take the absolute value of target since moving to position -5 requires the same number of steps as moving to position 5. We just need to mirror the moves.

  2. Initialize variables:

    • s = 0: tracks our current position (sum of all moves so far)
    • k = 0: counts the number of moves we've made
  3. Main loop: We continuously make moves in the positive direction:

    • Increment k by 1 (making the k-th move)
    • Add k to our position s (equivalent to moving right by k steps)
    • Check our stopping condition
  4. Stopping condition: We stop when both conditions are met:

    • s >= target: We've reached or passed the target
    • (s - target) % 2 == 0: The overshoot is even

    When both are true, we return k as our answer.

The algorithm works because:

  • If s == target, we've reached exactly where we want (overshoot is 0, which is even)
  • If s > target with even overshoot, we can flip certain previous moves to negative. Specifically, we need to flip moves that sum to (s - target) / 2

For example, if target = 2:

  • After move 1: s = 1, s < target, continue
  • After move 2: s = 1 + 2 = 3, s > target, overshoot = 3 - 2 = 1 (odd), continue
  • After move 3: s = 3 + 3 = 6, s > target, overshoot = 6 - 2 = 4 (even), stop

We return k = 3. The actual sequence would be +1 -2 +3 = 2 (we flipped move 2 since we needed to reduce our sum by 4, and flipping move 2 reduces it by 2 * 2 = 4).

The time complexity is O(sqrt(target)) since the sum 1 + 2 + ... + k grows quadratically, and space complexity is O(1).

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution for target = 5:

Step 1: Initialize

  • Convert target to absolute value: target = |5| = 5
  • Set s = 0 (current position), k = 0 (number of moves)

Step 2: Execute the main loop

Move 1:

  • Increment k: k = 1
  • Add to position: s = 0 + 1 = 1
  • Check conditions: s < target (1 < 5), so continue

Move 2:

  • Increment k: k = 2
  • Add to position: s = 1 + 2 = 3
  • Check conditions: s < target (3 < 5), so continue

Move 3:

  • Increment k: k = 3
  • Add to position: s = 3 + 3 = 6
  • Check conditions:
    • s >= target ✓ (6 >= 5)
    • (s - target) % 2 = (6 - 5) % 2 = 1 % 2 = 1 (odd) ✗
  • Overshoot is odd, so continue

Move 4:

  • Increment k: k = 4
  • Add to position: s = 6 + 4 = 10
  • Check conditions:
    • s >= target ✓ (10 >= 5)
    • (s - target) % 2 = (10 - 5) % 2 = 5 % 2 = 1 (odd) ✗
  • Overshoot is still odd, so continue

Move 5:

  • Increment k: k = 5
  • Add to position: s = 10 + 5 = 15
  • Check conditions:
    • s >= target ✓ (15 >= 5)
    • (s - target) % 2 = (15 - 5) % 2 = 10 % 2 = 0 (even) ✓
  • Both conditions met! Return k = 5

Step 3: Understanding the result

We need 5 moves to reach position 5. The overshoot was 10, meaning we need to flip moves that sum to 10 / 2 = 5. We can flip move 5 itself from positive to negative.

The actual sequence would be: +1 +2 +3 +4 -5 = 5

Verification: 1 + 2 + 3 + 4 - 5 = 10 - 5 = 5

Solution Implementation

1class Solution:
2    def reachNumber(self, target: int) -> int:
3        # Since we can mirror any sequence of moves, we only need to consider positive targets
4        target = abs(target)
5      
6        # Initialize sum of steps and step counter
7        current_sum = 0
8        step = 0
9      
10        # Keep adding steps until we meet the conditions
11        while True:
12            # We can reach the target when:
13            # 1. The sum of steps is at least the target
14            # 2. The difference between sum and target is even (we can flip some steps)
15            if current_sum >= target and (current_sum - target) % 2 == 0:
16                return step
17          
18            # Move to the next step
19            step += 1
20            current_sum += step
21
1class Solution {
2    public int reachNumber(int target) {
3        // Convert target to absolute value since we can reach negative positions 
4        // by flipping signs, so the problem is symmetric
5        target = Math.abs(target);
6      
7        // Initialize sum of steps and step counter
8        int currentSum = 0;
9        int stepCount = 0;
10      
11        // Keep adding steps until we find the minimum number of steps needed
12        while (true) {
13            // Check if we've reached or passed the target AND
14            // the difference between current sum and target is even
15            // (even difference means we can flip some steps to reach exact target)
16            if (currentSum >= target && (currentSum - target) % 2 == 0) {
17                return stepCount;
18            }
19          
20            // Increment step counter for next step
21            stepCount++;
22          
23            // Add current step value to the running sum
24            currentSum += stepCount;
25        }
26    }
27}
28
1class Solution {
2public:
3    int reachNumber(int target) {
4        // Convert target to absolute value since we can reach negative targets 
5        // by flipping signs (the problem is symmetric)
6        target = abs(target);
7      
8        // Initialize variables:
9        // sum: cumulative sum of steps (1 + 2 + 3 + ... + numSteps)
10        // numSteps: current number of steps taken
11        int sum = 0;
12        int numSteps = 0;
13      
14        // Keep adding steps until we find a valid solution
15        while (true) {
16            // Check if we've reached a valid state:
17            // 1. sum >= target: We've gone at least as far as the target
18            // 2. (sum - target) % 2 == 0: The difference is even, meaning we can
19            //    flip certain steps from positive to negative to reach exactly target
20            if (sum >= target && (sum - target) % 2 == 0) {
21                return numSteps;
22            }
23          
24            // Take another step
25            ++numSteps;
26            sum += numSteps;
27        }
28    }
29};
30
1/**
2 * Calculates the minimum number of moves to reach the target position on a number line
3 * Starting from position 0, in the k-th move, you can go left or right by k steps
4 * 
5 * @param target - The target position to reach
6 * @returns The minimum number of moves needed to reach the target
7 */
8function reachNumber(target: number): number {
9    // Work with absolute value since the problem is symmetric
10    target = Math.abs(target);
11  
12    // Initialize sum of steps and step counter
13    let sum: number = 0;
14    let step: number = 0;
15  
16    // Keep moving forward until we meet the conditions:
17    // 1. We've reached or passed the target
18    // 2. The difference between sum and target is even (can be achieved by flipping some moves)
19    while (true) {
20        // Check if we can reach the target by flipping some previous moves
21        // The difference must be even because flipping a move changes the sum by 2*moveValue
22        if (sum >= target && (sum - target) % 2 === 0) {
23            return step;
24        }
25      
26        // Increment step counter and add to sum
27        step++;
28        sum += step;
29    }
30}
31

Time and Space Complexity

Time Complexity: O(√|target|)

The algorithm increments k starting from 0 and accumulates the sum s = 1 + 2 + 3 + ... + k. The loop continues until s >= target and (s - target) % 2 == 0.

Since the sum of first k natural numbers is k(k+1)/2, we need to find the smallest k such that k(k+1)/2 >= |target|. Solving this inequality: k² + k - 2|target| >= 0, which gives us k ≈ √(2|target|) using the quadratic formula.

Even after reaching the target sum, we might need a few more iterations to satisfy the parity condition (s - target) % 2 == 0, but this adds at most a constant number of additional steps. Therefore, the number of iterations is proportional to √|target|.

Space Complexity: O(1)

The algorithm only uses a fixed number of variables (target, s, and k) regardless of the input size. No additional data structures or recursive calls are used, so the space complexity is constant.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Forgetting to Handle Negative Targets

One of the most common mistakes is not taking the absolute value of the target at the beginning. The problem allows negative targets, but due to symmetry, we should always work with positive values.

Incorrect approach:

def reachNumber(self, target: int) -> int:
    current_sum = 0
    step = 0
    # Missing abs(target) - will fail for negative targets
    while True:
        if current_sum >= target and (current_sum - target) % 2 == 0:
            return step
        step += 1
        current_sum += step

Solution: Always use target = abs(target) at the start.

2. Integer Overflow in Other Languages

While Python handles large integers gracefully, in languages like Java or C++, the sum calculation could overflow for very large targets.

Potential issue in C++/Java:

int current_sum = 0;  // Could overflow for large targets

Solution: Use long/long long data types or implement overflow checking:

long long current_sum = 0;

3. Misunderstanding the Stopping Condition

Some might think we need to stop as soon as current_sum >= target, missing the even difference requirement.

Incorrect logic:

while current_sum < target:  # Wrong! Misses the even difference check
    step += 1
    current_sum += step
return step

This would fail for cases like target = 2, where after 2 steps we have sum = 3 (overshoot by 1, which is odd).

Solution: Always check both conditions: current_sum >= target AND (current_sum - target) % 2 == 0

4. Off-by-One Error in Step Counting

Some implementations might incorrectly update the step counter, leading to off-by-one errors.

Incorrect ordering:

while True:
    current_sum += step  # Using step before incrementing it
    step += 1
    if current_sum >= target and (current_sum - target) % 2 == 0:
        return step  # Returns wrong value

Solution: Increment step first, then add to sum, or ensure you return the correct step count based on your implementation order.

5. Trying to Track Actual Moves

Attempting to maintain the actual sequence of moves (which steps go left/right) adds unnecessary complexity and memory usage.

Overcomplicated approach:

moves = []  # Unnecessary - we don't need to track actual moves
for i in range(1, n+1):
    if should_go_right(i):
        moves.append(('R', i))
    else:
        moves.append(('L', i))

Solution: Focus only on the mathematical property - we just need the count, not the actual sequence. The sequence can be reconstructed if needed by flipping appropriate moves.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More