title: Check Knight Tour Configuration slug: check-knight-tour-configuration difficulty: medium leetcode_id: 2596 leetcode_url: https://leetcode.com/problems/check-knight-tour-configuration/ categories: - arrays - graphs patterns: - slug: matrix-traversal is_optimal: true function_signature: "def check_valid_grid(grid: list[list[int]]) -> bool:" test_cases: visible: - input: { grid: [[0, 11, 16, 5, 20], [17, 4, 19, 10, 15], [12, 1, 8, 21, 6], [3, 18, 23, 14, 9], [24, 13, 2, 7, 22]] } expected: true - input: { grid: [[0, 3, 6], [5, 8, 1], [2, 7, 4]] } expected: false hidden: - input: { grid: [[0, 2, 4], [5, 7, 1], [6, 3, 8]] } expected: false - input: { grid: [[1, 0, 2], [3, 4, 5], [6, 7, 8]] } expected: false - input: { grid: [[0, 5, 4], [3, 2, 7], [6, 1, 8]] } expected: false description: | There is a knight on an `n x n` chessboard. In a valid configuration, the knight starts **at the top-left cell** of the board and visits every cell on the board **exactly once**. You are given an `n x n` integer matrix `grid` consisting of distinct integers from the range `[0, n * n - 1]` where `grid[row][col]` indicates that the cell `(row, col)` is the `grid[row][col]`th cell that the knight visited. The moves are **0-indexed**. Return `true` *if* `grid` *represents a valid configuration of the knight's movements or* `false` *otherwise*. **Note** that a valid knight move consists of moving two squares vertically and one square horizontally, or two squares horizontally and one square vertically. constraints: | - `n == grid.length == grid[i].length` - `3 <= n <= 7` - `0 <= grid[row][col] < n * n` - All integers in `grid` are **unique** examples: - input: "grid = [[0,11,16,5,20],[17,4,19,10,15],[12,1,8,21,6],[3,18,23,14,9],[24,13,2,7,22]]" output: "true" explanation: "The grid represents a valid knight's tour. Starting from position (0,0) with value 0, the knight makes valid L-shaped moves to visit every cell exactly once." - input: "grid = [[0,3,6],[5,8,1],[2,7,4]]" output: "false" explanation: "The 8th move of the knight is not valid considering its position after the 7th move. The knight cannot reach the cell marked 8 from the cell marked 7 using a valid knight move." explanation: intuition: | Think of this problem as verifying a recorded sequence of moves rather than finding one. The grid tells us the **order** in which each cell was visited. Cell with value `0` was visited first, cell with value `1` was visited second, and so on. Our job is to verify that each consecutive pair of visits (from cell `k` to cell `k+1`) represents a valid knight move. Imagine you're watching a replay of a chess game. You see where the knight was on move 0, move 1, move 2, etc. To verify it's valid, you just need to check: "Could a knight actually jump from the move-0 position to the move-1 position? From move-1 to move-2?" and so on. A knight moves in an "L" shape: two squares in one direction and one square perpendicular to that. This gives us exactly 8 possible moves from any position. The key insight is that the **absolute difference** between row positions must be paired with a complementary difference in column positions: either (2,1) or (1,2). approach: | We solve this using **Position Mapping and Sequential Validation**: **Step 1: Build a position lookup** - Create a mapping from move number to `(row, col)` position - Iterate through the grid and store `positions[grid[r][c]] = (r, c)` for each cell - This lets us quickly find where the knight was on any given move   **Step 2: Validate the starting position** - Check that `grid[0][0] == 0` — the knight must start at the top-left cell - If this fails, return `false` immediately   **Step 3: Verify each consecutive move** - For each move number from `0` to `n*n - 2`: - Get the position of move `k` and move `k+1` - Calculate the absolute row difference `dr` and column difference `dc` - A valid knight move requires either `(dr, dc) = (1, 2)` or `(dr, dc) = (2, 1)` - If any move is invalid, return `false`   **Step 4: Return the result** - If all consecutive moves are valid, return `true` common_pitfalls: - title: Forgetting the Starting Position Check description: | The problem states the knight must start at the **top-left cell**. This means `grid[0][0]` must equal `0`. Some solutions skip this check and only validate the moves, but a grid where the knight starts elsewhere (like `grid[1][1] = 0`) would be invalid even if all moves are correct knight moves. wrong_approach: "Only checking if consecutive moves are valid knight moves" correct_approach: "First verify grid[0][0] == 0, then validate all moves" - title: Checking All 8 Directions Instead of Just Validating Distances description: | You don't need to enumerate all 8 knight move directions and check if the next position matches one of them. The simpler approach is to compute the absolute row and column differences between consecutive positions. A valid knight move has distances that are either `(1, 2)` or `(2, 1)`. wrong_approach: "Generating all 8 possible next positions and checking membership" correct_approach: "Check if (abs(dr), abs(dc)) forms a valid knight move pattern" - title: Off-by-One in Move Iteration description: | The grid contains values from `0` to `n*n - 1`. When checking consecutive moves, you iterate from move `0` to move `n*n - 2` (not `n*n - 1`), comparing each move `k` with move `k+1`. Going up to `n*n - 1` would cause an index-out-of-bounds error when accessing move `n*n`. key_takeaways: - "**Position mapping**: Converting grid values to coordinates enables O(1) lookup for any move number" - "**Knight move validation**: A knight move is valid if the absolute differences in coordinates are `(1, 2)` or `(2, 1)`" - "**Sequential verification**: When validating a path, check each consecutive pair rather than trying to reconstruct the entire path" - "**Constraint checking**: Always verify initial conditions (starting position) before validating the sequence" time_complexity: "O(n^2). We iterate through all n*n cells once to build the position map, then iterate through n*n - 1 consecutive pairs to validate moves." space_complexity: "O(n^2). We store a position lookup containing n*n entries mapping move numbers to coordinates." solutions: - approach_name: Position Mapping is_optimal: true code: | def check_valid_grid(grid: list[list[int]]) -> bool: n = len(grid) # Knight must start at top-left corner if grid[0][0] != 0: return False # Build position lookup: move number -> (row, col) positions = {} for r in range(n): for c in range(n): positions[grid[r][c]] = (r, c) # Verify each consecutive move is a valid knight move for move in range(n * n - 1): r1, c1 = positions[move] r2, c2 = positions[move + 1] # Calculate absolute differences dr = abs(r2 - r1) dc = abs(c2 - c1) # Valid knight move: (1,2) or (2,1) if not ((dr == 1 and dc == 2) or (dr == 2 and dc == 1)): return False return True explanation: | **Time Complexity:** O(n^2) — We scan the grid once to build the lookup, then validate n^2 - 1 moves. **Space Complexity:** O(n^2) — The position dictionary stores one entry per cell. We first ensure the knight starts at (0,0), then build a mapping from move numbers to positions. Finally, we check each consecutive pair of moves to verify they form valid knight moves. - approach_name: Direct Grid Search is_optimal: false code: | def check_valid_grid(grid: list[list[int]]) -> bool: n = len(grid) # Knight must start at top-left corner if grid[0][0] != 0: return False # All 8 possible knight moves knight_moves = [ (-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1) ] # Find position of move 0 (must be at 0,0) row, col = 0, 0 # Verify each move from 0 to n*n-2 for move in range(n * n - 1): found = False # Try all 8 knight moves to find the next position for dr, dc in knight_moves: nr, nc = row + dr, col + dc # Check bounds and if this is the next move if 0 <= nr < n and 0 <= nc < n and grid[nr][nc] == move + 1: row, col = nr, nc found = True break if not found: return False return True explanation: | **Time Complexity:** O(n^2) — We check up to 8 neighbors for each of the n^2 moves. **Space Complexity:** O(1) — We only store the current position and constants. This approach follows the knight move-by-move. Starting from (0,0), we search all 8 possible knight destinations to find the cell containing the next move number. While this uses less space, it requires searching neighbors at each step.