Minimum Knight Moves is the classic "shortest path on an unweighted board" problem. A chess knight jumps in an L, and we want the fewest jumps to reach a target square. The single most important idea here: when every move costs the same, breadth-first search (BFS) finds the shortest path.
Problem. A knight sits on square start = (0, 0) of an N x N board. From any square it may make
one of 8 L-shaped moves. Return the minimum number of moves to reach target. If it is unreachable,
return -1.
Example: N = 5, start = (0, 0), target = (3, 3) → answer 2 (one route is (0,0) → (1,2) → (3,3)).
The slow way first
You could try to be greedy — always hop "toward" the target. But the knight moves in Ls, so a square that looks closer in straight-line distance can actually be farther in knight moves. Greedy guessing gets the wrong answer.
You could also explore every possible sequence of jumps with plain recursion (DFS), but that re-walks the same squares over and over and explodes exponentially. The question to ask: how do I find a shortest path when every step costs exactly one move?
The idea: spread outward in rings
Treat each square as a node and each legal knight jump as an edge. Every edge has the same cost (one move), so the graph is unweighted — and on an unweighted graph, BFS gives the shortest path for free.
BFS visits squares in order of distance: first everything reachable in 0 moves (just the start), then everything in 1 move, then 2, and so on. The wavefront spreads outward like a ripple. The first time the wavefront touches the target, that distance is the minimum.
The trick that keeps it fast: a seen set. We mark a square the moment we enqueue it, so it never enters
the queue twice. That caps total work at one visit per square.
Walk through it
Step through the animation. The start square S lights up first (distance 0). Layer 1 fills the two
on-board L-jumps from S. Layer 2 expands those, and one of them — (1,2) — lands a knight jump exactly
on the target T = (3,3). The moment we dequeue T, we return its recorded distance: 2.
Pseudocode
queue starts with (start, distance 0)
seen = {start}
while the queue is not empty:
(square, d) = pop the front of the queue
if square == target:
return d # first arrival is the shortest
for each of the 8 knight offsets:
next = square + offset
if next is on the board and not in seen:
add next to seen # never enqueue a square twice
push (next, d + 1)
return -1 # unreachableThe Python solution
def min_knight_moves(start, target):
OFFSETS = [(1, 2), (2, 1), (-1, 2), (-2, 1),
(1, -2), (2, -1), (-1, -2), (-2, -1)]
q = deque([(start, 0)])
seen = {start}
while q:
(r, c), d = q.popleft()
if (r, c) == target:
return d
for dr, dc in OFFSETS:
nr, nc = r + dr, c + dc
if 0 <= nr < N and 0 <= nc < N and (nr, nc) not in seen:
seen.add((nr, nc))
q.append(((nr, nc), d + 1))
return -1OFFSETSlists the 8 L-shaped knight moves once, so we can apply them in a loop.qis a FIFO queue of(square, distance)pairs; popping from the front (popleft) is what makes this breadth-first rather than depth-first.seenrecords every square we have ever enqueued so we never revisit one — this keeps each square to a single visit.- The check
(r, c) == targethappens when we pop, and because BFS pops in distance order, the first match is guaranteed minimal. - We only enqueue a neighbor when it is on the board and not in
seen, recording its distance asd + 1.
Complexity
| Case | Time | Notes |
|---|---|---|
| Greedy / DFS guessing | exponential (moderate) | re-walks squares, may be wrong |
| BFS (this solution) | O(N^2) (slow) | each square visited once, 8 edges each |
O(N^2) (slow)On an N x N board there are N^2 squares, and BFS visits each at most once with a constant 8 neighbors,
so it runs in O(N^2) time and uses O(N^2) space for the queue and seen set. That "every move costs
one, so use BFS" trade is one of the most reusable patterns in interviews.
When this pattern shows up
Whenever a problem asks for the fewest steps / shortest path and every move costs the same, reach for BFS — not DFS, not Dijkstra. Word Ladder, rotten oranges, shortest path in a binary matrix, open-the- lock, and knight moves are all the same move: expand a queue layer by layer and stop on first arrival.
Mark a square as seen when you enqueue it, not when you dequeue it. If you wait until dequeue, the same square can be pushed many times before it is first popped, and the queue can blow up. Also remember to bound the offsets to the board, or the knight will "jump off the edge."
Practice
For N = 5, start = (0,0), target = (3,3): which BFS layer first contains the target, and what does that make the answer?
1. Why does BFS (not DFS) give the minimum number of knight moves?
2. When should a square be added to the seen set?
3. How many candidate moves does the knight try from each square?
4. What is the time complexity of BFS on an N x N board here?