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:
  - matrix-traversal

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]`<sup>th</sup> 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

    &nbsp;

    **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

    &nbsp;

    **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`

    &nbsp;

    **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.
