A knight is placed on a chessboard of size n x n
. Find a sequence of moves for the knight so that it visits each square exactly once.
We represent the chessboard as a 2D array where (0, 0)
is the top-left square and (n-1, n-1)
is the bottom-right square.
The knight can move in eight possible directions:
A valid tour is a sequence of moves such that:
(0, 0)
.You need to implement a function that takes the size of the chessboard n
as input and returns a 2D array representing a valid knight's tour. If a valid tour is not possible, return an empty array []
.
The returned array should be an n x n
matrix where each cell contains the order in which the knight visited that square (starting from 0). If a solution exists, there will only be one valid solution.
Example 1:
Input: n = 5
Output:
[
[0, 3, 14, 17, 8],
[13, 16, 7, 2, 5],
[4, 1, 18, 9, 12],
[15, 6, 11, 24, 21],
[20, 23, 22, 19, 10]
]
Explanation:
The knight starts at (0, 0), visits (0, 3) next, then (2, 4), and so on until it has visited all the cells in the board exactly once.
Example 2:
Input: n = 2
Output: []
Explanation:
For a 2x2 board, it's impossible for the knight to visit all cells exactly once.
Constraints:
1 <= n <= 8
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The Knight's Tour problem is about finding a sequence of moves for a knight on a chessboard so it visits every square exactly once. A brute-force approach to this problem involves trying every possible move sequence until we find one that works.
Here's how the algorithm would work step-by-step:
def knights_tour_brute_force(board_size, start_row, start_column):
board = [[-1 for _ in range(board_size)] for _ in range(board_size)]
row_moves = [2, 1, -1, -2, -2, -1, 1, 2]
column_moves = [1, 2, 2, 1, -1, -2, -2, -1]
board[start_row][start_column] = 0
def is_valid_move(row, column, chess_board):
return (0 <= row < board_size and 0 <= column < board_size and
chess_board[row][column] == -1)
def solve_knights_tour(move_number, current_row, current_column, chess_board):
if move_number == board_size * board_size:
return True
# Iterate through all possible knight moves.
for move_index in range(8):
new_row = current_row + row_moves[move_index]
new_column = current_column + column_moves[move_index]
# Check if the move is valid.
if is_valid_move(new_row, new_column, chess_board):
chess_board[new_row][new_column] = move_number
# Recursively solve the rest of the tour.
if solve_knights_tour(move_number + 1, new_row, new_column, chess_board):
return True
# Backtrack if the current move doesn't lead to a solution.
chess_board[new_row][new_column] = -1
return False
# Start the recursive solution.
if solve_knights_tour(1, start_row, start_column, board):
return board
else:
return None
The Knight's Tour problem asks you to find a sequence of moves for a knight on a chessboard so that it visits every square exactly once. The optimal approach uses a specific strategy to guide the knight's movement, making it much faster than just trying random moves.
Here's how the algorithm would work step-by-step:
def knights_tour(board_size):
def get_possible_moves(current_position):
row, col = current_position
possible_moves = [
(row - 2, col - 1),
(row - 2, col + 1),
(row - 1, col - 2),
(row - 1, col + 2),
(row + 1, col - 2),
(row + 1, col + 2),
(row + 2, col - 1),
(row + 2, col + 1),
]
valid_moves = [
(row_new, col_new)
for row_new, col_new in possible_moves
if 0 <= row_new < board_size and 0 <= col_new < board_size
]
return valid_moves
def get_number_of_available_moves(position, board_state):
possible_next_moves = get_possible_moves(position)
available_moves_count = 0
for next_row, next_col in possible_next_moves:
if board_state[next_row][next_col] == -1:
available_moves_count += 1
return available_moves_count
def find_tour(start_row, start_col):
board_state = [([-1] * board_size) for _ in range(board_size)]
row, col = start_row, start_col
board_state[row][col] = 0
move_number = 1
for _ in range(board_size * board_size - 1):
# Warnsdorff's Rule: Prioritize squares with fewer onward moves.
possible_moves = get_possible_moves((row, col))
valid_moves = [
(row_new, col_new)
for row_new, col_new in possible_moves
if board_state[row_new][col_new] == -1
]
if not valid_moves:
return None # No valid moves, tour failed.
move_counts = []
for next_row, next_col in valid_moves:
move_counts.append(
get_number_of_available_moves((next_row, next_col), board_state)
)
# Move to the position with the fewest onward moves
min_moves = min(move_counts)
best_moves = [
valid_moves[i]
for i, count in enumerate(move_counts)
if count == min_moves
]
row, col = best_moves[0]
board_state[row][col] = move_number
move_number += 1
return board_state # Return the board if the tour is successful
# Try starting from a corner.
start_positions = [(0, 0), (0, board_size - 1), (board_size - 1, 0), (board_size - 1, board_size - 1)]
for start_row, start_col in start_positions:
tour = find_tour(start_row, start_col)
if tour:
return tour
return None #If all start positions failed, return none
Case | How to Handle |
---|---|
n <= 0 or n == 1 | Return empty array since a knight's tour is impossible on boards of this size. |
n == 2 | Return empty array since a knight's tour is impossible on a 2x2 board. |
n == 3 | Return empty array since a knight's tour is impossible on a 3x3 board. |
No valid tour exists for the given n | Backtracking algorithm should exhaust all possibilities and return an empty array if no solution is found. |
Stack Overflow for large n if recursion is not optimized | Optimize recursion through tail call optimization if available or use iterative approach to avoid stack overflow. |
Integer overflow for very large n causing index out of bounds | Use appropriate data types (e.g., long) to store board size and move numbers to prevent potential overflow. |
Algorithm visits the same square twice | Backtracking condition should prevent revisits, and ensure the algorithm explores other paths to find a valid solution. |
Memory constraints for excessively large n | If possible, consider space-optimized data structures or algorithms (although likely impractical for this problem on very huge n). |