Reach a Number solution and explanation
9 more videos on this problem
Reach a Number | Live Coding with Explanation | Leetcode #754Algorithms Made Easy · 5:49 · 13K views
Reach a Number | reach a number | leetcode 754Naresh Gupta · 9:59 · 6K views
LC 754: Reach a number, Part 1Dinesh Kumar · 8:19 · 5K views
754 - Reach a Number - LeetCode (Medium) | Maths | Binary Search | O(logN) Most Optimal SolutionThe Lame Coder · 26:42 · 3K views
花花酱 LeetCode 754. Reach a Number - 刷题找工作 EP147Hua Hua · 13:55 · 2K views
LeetCode 754. Reach a NumberHappy Coding · 16:32 · 1K views
LeetCode Tutorial 754: Reach a NumberHuaHua System Design · 10:38 · 920 views
LC 754: Reach a number, Part 2(code)Dinesh Kumar · 2:02 · 892 views
Leetcode 754. Reach a NumberAlgorithms Casts · 13:27 · 882 views
Leetcode - Reach a Number (Python)Timothy H Chang · 9:04 · 852 views
The problem
You are standing at position 0 on an infinite number line. There is a destination at position target.
You can make some number of moves numMoves so that:
- On each move, you can either go left or right.
- During the
ithmove (starting fromi == 1toi == numMoves), you takeisteps in the chosen direction.
Given the integer target, return the minimum number of moves required (i.e., the minimum numMoves) to reach the destination.
Example 1:
Input: target = 2 Output: 3 Explanation: On the 1st move, we step from 0 to 1 (1 step). On the 2nd move, we step from 1 to -1 (2 steps). On the 3rd move, we step from -1 to 2 (3 steps).
Example 2:
Input: target = 3 Output: 2 Explanation: On the 1st move, we step from 0 to 1 (1 step). On the 2nd move, we step from 1 to 3 (2 steps).
Constraints:
-109 <= target <= 109target != 0
Approach overview
Problem Overview: You start at position 0 on an infinite number line. On the kth move you can go either left or right by exactly k steps. The goal is to reach a given target using the minimum number of moves.
Approach 1: Divide and Conquer / Binary Search on Steps (O(sqrt(n)) time, O(1) space)
The key observation comes from math. If you take k moves, the maximum distance you can reach in one direction is the triangular sum 1 + 2 + ... + k = k(k+1)/2. Instead of simulating every path, compute the smallest k such that this sum is at least |target|. If the difference sum - target is even, you can flip the direction of some moves to correct the overshoot. If it is odd, increase k until the parity becomes even. You can find the minimal valid k by iteratively increasing steps or applying binary search over possible move counts. The divide-and-conquer thinking comes from reducing the problem to finding the smallest step count that satisfies both the distance and parity constraints.
Approach 2: Dynamic Programming (O(n * sqrt(n)) time, O(n) space)
A dynamic programming approach models reachable positions after each move. Define a DP state representing positions reachable after k moves. For each step, you transition from previous positions by adding or subtracting k. This effectively explores the state space layer by layer until the target appears. Because the reachable range grows roughly proportional to the triangular number sequence, the number of states expands quickly, making this solution less practical for large targets. However, it demonstrates the brute-force search space clearly and helps reason about why parity and cumulative sums determine reachability.
Recommended for interviews: The mathematical step-sum solution is what interviewers expect. It reduces the problem to analyzing triangular numbers and parity instead of exploring all paths. Starting with a DP or brute-force explanation shows understanding of the state space, but recognizing the k(k+1)/2 distance property and parity adjustment demonstrates stronger algorithmic insight.
Approach 1: Approach 1: Divide and Conquer
This approach involves breaking down the problem into smaller subproblems, solving each subproblem recursively, and then combining the results. This is a classic Divide and Conquer approach which can be applied to a variety of problems, such as sorting algorithms (e.g., Merge Sort).
def merge(arr, left, mid, right):
n1 = mid - left + 1
n2 = right - mid
L = arr[left:left + n1]
R = arr[mid + 1:mid + 1 + n2]
i = 0
j = 0
k = left
while i < n1 and j < n2:
if L[i] <= R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
while i < n1:
arr[k] = L[i]
i += 1
k += 1
while j < n2:
arr[k] = R[j]
j += 1
k += 1
def mergeSort(arr, left, right):
if left < right:
mid = (left + right) // 2
mergeSort(arr, left, mid)
mergeSort(arr, mid + 1, right)
merge(arr, left, mid, right)
arr = [12, 11, 13, 5, 6, 7]
mergeSort(arr, 0, len(arr) - 1)
print("Sorted array:")
print(arr)public class MergeSort {
void merge(int arr[], int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
int L[] = new int[n1];
int R[] = new int[n2];
for (int i = 0; i < n1; i++)
L[i] = arr[left + i];
for (int j = 0; j < n2; j++)
R[j] = arr[mid + 1 + j];
int i = 0, j = 0;
int k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
public static void main(String args[]) {
int arr[] = {12, 11, 13, 5, 6, 7};
MergeSort ob = new MergeSort();
ob.mergeSort(arr, 0, arr.length - 1);
System.out.println("Sorted array:");
for (int i = 0; i < arr.length; i++)
System.out.print(arr[i] + " ");
}
}#include <iostream>
using namespace std;
void merge(int arr[], int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
int L[n1], R[n2];
for (int i = 0; i < n1; i++)
L[i] = arr[left + i];
for (int j = 0; j < n2; j++)
R[j] = arr[mid + 1 + j];
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int arr_size = sizeof(arr) / sizeof(arr[0]);
mergeSort(arr, 0, arr_size - 1);
cout << "Sorted array: \n";
for (int i = 0; i < arr_size; i++)
cout << arr[i] << " ";
return 0;
}function merge(arr, left, mid, right) {
const n1 = mid - left + 1;
const n2 = right - mid;
const L = new Array(n1);
const R = new Array(n2);
for (let i = 0; i < n1; i++)
L[i] = arr[left + i];
for (let j = 0; j < n2; j++)
R[j] = arr[mid + 1 + j];
let i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
function mergeSort(arr, left, right) {
if (left < right) {
const mid = left + Math.floor((right - left) / 2);
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
let arr = [12, 11, 13, 5, 6, 7];
mergeSort(arr, 0, arr.length - 1);
console.log("Sorted array:", arr);#include <stdio.h>
void merge(int arr[], int left, int mid, int right) {
int i, j, k;
int n1 = mid - left + 1;
int n2 = right - mid;
int L[n1], R[n2];
for (i = 0; i < n1; i++)
L[i] = arr[left + i];
for (j = 0; j < n2; j++)
R[j] = arr[mid + 1 + j];
i = 0;
j = 0;
k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
int main() {
int arr[] = {12, 11, 13, 5, 6, 7};
int arr_size = sizeof(arr) / sizeof(arr[0]);
mergeSort(arr, 0, arr_size - 1);
printf("Sorted array: \n");
for (int i = 0; i < arr_size; i++)
printf("%d ", arr[i]);
return 0;
}using System;
class MergeSort {
void Merge(int[] arr, int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
int[] L = new int[n1];
int[] R = new int[n2];
for (int i = 0; i < n1; ++i)
L[i] = arr[left + i];
for (int j = 0; j < n2; ++j)
R[j] = arr[mid + 1 + j];
int iIndex = 0, jIndex = 0;
int k = left;
while (iIndex < n1 && jIndex < n2) {
if (L[iIndex] <= R[jIndex]) {
arr[k] = L[iIndex];
iIndex++;
} else {
arr[k] = R[jIndex];
jIndex++;
}
k++;
}
while (iIndex < n1) {
arr[k] = L[iIndex];
iIndex++;
k++;
}
while (jIndex < n2) {
arr[k] = R[jIndex];
jIndex++;
k++;
}
}
void MergeSortFunction(int[] arr, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
MergeSortFunction(arr, left, mid);
MergeSortFunction(arr, mid + 1, right);
Merge(arr, left, mid, right);
}
}
public static void Main(String[] args) {
int[] arr = {12, 11, 13, 5, 6, 7};
MergeSort ob = new MergeSort();
ob.MergeSortFunction(arr, 0, arr.Length - 1);
Console.WriteLine("Sorted array:");
Console.WriteLine(string.Join(" ", arr));
}
}This Python code applies the Merge Sort algorithm using recursion. It consists of a merge function that combines two sorted halves into a sorted array and a mergeSort function that recursively sorts the array halves.
Time Complexity: O(n log n)
Space Complexity: O(n)
Approach 2: Approach 2: Dynamic Programming
Dynamic Programming (DP) is an approach that solves complex problems by breaking them down into simpler subproblems and storing the results to avoid recomputing. It's particularly useful for optimization problems where decisions depend on previous decisions.
def fib(n, dp):
if n <= 1:
return n
if dp[n] != -1:
return dp[n]
dp[n] = fib(n-1, dp) + fib(n-2, dp)
return dp[n]
n = 10
dp = [-1] * (n + 1)
print("Fibonacci number is", fib(n, dp))import java.util.Arrays;
class Fibonacci {
public int fib(int n, int[] dp) {
if (n <= 1) return n;
if (dp[n] != -1) return dp[n];
return dp[n] = fib(n - 1, dp) + fib(n - 2, dp);
}
public static void main(String[] args) {
int n = 10;
int[] dp = new int[n + 1];
Arrays.fill(dp, -1);
Fibonacci obj = new Fibonacci();
System.out.println("Fibonacci number is " + obj.fib(n, dp));
}
}#include <iostream>
#include <vector>
using namespace std;
int fib(int n, vector<int>& dp) {
if (n <= 1)
return n;
if (dp[n] != -1)
return dp[n];
return dp[n] = fib(n - 1, dp) + fib(n - 2, dp);
}
int main() {
int n = 10;
vector<int> dp(n + 1, -1);
cout << "Fibonacci number is " << fib(n, dp) << endl;
return 0;
}function fib(n, dp) {
if (n <= 1) return n;
if (dp[n] !== -1) return dp[n];
dp[n] = fib(n-1, dp) + fib(n-2, dp);
return dp[n];
}
let n = 10;
let dp = Array(n + 1).fill(-1);
console.log("Fibonacci number is", fib(n, dp));#include <stdio.h>
#include <string.h>
int fib(int n, int dp[]) {
if (n <= 1)
return n;
if (dp[n] != -1)
return dp[n];
return dp[n] = fib(n - 1, dp) + fib(n - 2, dp);
}
int main() {
int n = 10;
int dp[n + 1];
memset(dp, -1, sizeof(dp));
printf("Fibonacci number is %d\n", fib(n, dp));
return 0;
}using System;
class Fibonacci {
int Fib(int n, int[] dp) {
if (n <= 1) return n;
if (dp[n] != -1) return dp[n];
return dp[n] = Fib(n-1, dp) + Fib(n-2, dp);
}
public static void Main(string[] args) {
int n = 10;
int[] dp = new int[n + 1];
Array.Fill(dp, -1);
Fibonacci fibonacci = new Fibonacci();
Console.WriteLine("Fibonacci number is " + fibonacci.Fib(n, dp));
}
}This Python code demonstrates the use of dynamic programming to calculate Fibonacci numbers. The function leverages memoization by storing the results of previous computations in the dp list to avoid redundant calculations.
Time Complexity: O(n)
Space Complexity: O(n)
Approach 3: Mathematical Analysis
Due to symmetry, each time we can choose to move left or right, so we can take the absolute value of target.
Define s as the current position, and use the variable k to record the number of moves. Initially, both s and k are 0.
We keep adding to s in a loop until s \ge target and (s - target) bmod 2 = 0. At this point, the number of moves k is the answer, and we return it directly.
Why? Because if s \ge target and (s - target) bmod 2 = 0, we only need to change the sign of the positive integer \frac{s - target}{2} to negative, so that s equals target. Changing the sign of a positive integer essentially means changing the direction of the move, but the actual number of moves remains the same.
The time complexity is O(\sqrt{\left | target \right | }), and the space complexity is O(1).
func reachNumber(target int) int {
if target < 0 {
target = -target
}
var s, k int
for {
if s >= target && (s-target)%2 == 0 {
return k
}
k++
s += k
}
}Complexity comparison
| Approach | Complexity |
|---|---|
| Approach 1: Divide and Conquer | Time Complexity: O(n log n) |
| Approach 2: Dynamic Programming | Time Complexity: O(n) |
| Mathematical Analysis | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Divide and Conquer / Binary Search on Step Count | O(sqrt(n)) | O(1) | Optimal solution. Uses triangular sum and parity insight to compute minimal moves efficiently. |
| Dynamic Programming | O(n * sqrt(n)) | O(n) | Useful for understanding the reachable state space or building intuition before deriving the math optimization. |
Frequently asked questions
Is Reach a Number easy or hard?
Reach a Number is rated Medium because the brute-force idea is simple but the optimal insight is mathematical. Candidates who recognize the triangular number pattern and parity adjustment can solve it quickly, while others may struggle with exploring all left/right move combinations.
Reach a Number Python/Java solution
Python and Java implementations usually compute the minimal step count k such that k(k+1)/2 >= |target|. Then they adjust k until the difference between the triangular sum and target is even. The algorithm uses constant space and only a small loop to reach the correct step count.
How to solve Reach a Number in O(n)?
A straightforward simulation increments the step size and keeps adding moves until the cumulative sum reaches or exceeds the target. After that, check whether the difference between the sum and target is even; if not, continue adding steps until the parity condition holds. This linear stepping process effectively finds the minimal valid k.
What is the best approach for Reach a Number?
The optimal approach uses a mathematical observation about triangular numbers. Find the smallest k where the sum 1 + 2 + ... + k is at least |target|. If the difference between the sum and the target is even, you can flip directions of some moves to land exactly on the target. This runs in O(sqrt(n)) time and O(1) space.
Is Reach a Number asked at Google/Amazon/Meta?
Reach a Number is a common medium-level math puzzle used in technical interviews at companies like Google and Amazon. It tests whether candidates recognize patterns in cumulative sums and parity instead of attempting brute-force search across directions.
What data structure is used in Reach a Number?
The optimal solution does not rely on complex data structures. It primarily uses mathematical reasoning with integers and cumulative sums. Alternative explanations sometimes use dynamic programming to track reachable positions, but the interview-preferred method uses simple arithmetic.
What is the time complexity of Reach a Number?
The optimal solution runs in O(sqrt(n)) time because the triangular number k(k+1)/2 grows quadratically, so only about sqrt(target) steps are checked. Space complexity is O(1) since only a few variables are needed to track the cumulative sum and step count.
