Minimum Knight Moves
Medium · Graphs · Explanation and code example
DFS (Depth-First Search) with Memoization
How it works
Due to the knight's movement symmetry, we only need to consider the first quadrant (positive x and y). The minimum moves to reach (-x, y), (x, -y), or (-x, -y) are the same as reaching (x, y).
We can define a recursive relation: the minimum moves to reach (x, y) equals 1 plus the minimum of reaching (|x-1|, |y-2|) or (|x-2|, |y-1|). The absolute values keep us in the first quadrant, and memoization prevents redundant calculations.
Steps
- Take absolute values of
xandyto work in the first quadrant. - Define base cases:
(0, 0)requires0moves; positions wherex+y = 2require2moves. - For other positions, recursively compute:
min(dfs(|x-1|, |y-2|), dfs(|x-2|, |y-1|)) + 1. - Use memoization to cache results for each
(x, y)pair. - Return the computed minimum moves.
Complexity
- Time complexity:
O(|x × y|) - Space complexity:
O(|x × y|)
Where
(x,y)is the coordinate of the target.
Things to watch for
Not Exploiting Symmetry to Reduce Search Space
The knight's movement is symmetric across both axes. Searching the entire coordinate plane when only the first quadrant (using absolute values of x and y) is necessary wastes time and memory. Always convert to positive coordinates first.
Unbounded BFS Without Coordinate Limits
Without restricting the search area, BFS can explore positions far from both origin and target, leading to memory exhaustion. The optimal path rarely strays far beyond the target coordinates, so limiting exploration to a reasonable bounding box improves efficiency.
Missing Base Cases in Memoized DFS
The recursive solution requires proper base cases. The position (0, 0) needs 0 moves, and positions where x + y = 2 (like (1, 1), (2, 0), (0, 2)) need exactly 2 moves. Missing these base cases causes infinite recursion or incorrect results.
Python code example
from functools import lru_cache
class Solution:
def minKnightMoves(self, x: int, y: int) -> int:
@lru_cache(maxsize=None)
def dfs(x, y):
if x + y == 0:
# base case: (0, 0)
return 0
elif x + y == 2:
# base case: (1, 1), (0, 2), (2, 0)
return 2
else:
return min(dfs(abs(x - 1), abs(y - 2)), dfs(abs(x - 2), abs(y - 1))) + 1
return dfs(abs(x), abs(y))
C++ code example
class Solution {
private:
unordered_map<string, int> memo;
int dfs(int x, int y) {
string key = to_string(x) + "," + to_string(y);
if (memo.find(key) != memo.end()) {
return memo[key];
}
if (x + y == 0) {
return 0;
} else if (x + y == 2) {
return 2;
} else {
int ret = min(dfs(abs(x - 1), abs(y - 2)),
dfs(abs(x - 2), abs(y - 1))) + 1;
memo[key] = ret;
return ret;
}
}
public:
int minKnightMoves(int x, int y) {
return dfs(abs(x), abs(y));
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.