AlgoMaster Logo

Valid Sudoku

Last Updated: March 22, 2026

medium
4 min read

Understanding the Problem

We have a 9x9 Sudoku grid, and we need to check whether the current state of the board violates any of the three Sudoku rules. We are not solving the puzzle or checking if it can be solved. We just need to verify that no digit appears twice in the same row, the same column, or the same 3x3 sub-box.

The board may have empty cells (represented by '.'), and those are simply ignored. The real question is: among the filled cells, is there any duplicate where there shouldn't be one?

The key challenge is figuring out how to efficiently check all three constraints. Rows and columns are straightforward, but the 3x3 sub-box check requires a way to map each cell to its corresponding box.

Key Constraints:

  • board.length == 9 and board[i].length == 9 means the board is always a fixed 9x9 grid. The total number of cells is always 81, a constant. Any solution that iterates over the board is effectively O(1) in terms of input size.
  • board[i][j] is a digit 1-9 or '.' means we don't need to handle invalid characters. Empty cells are marked with '.'.

Approach 1: Three Separate Passes

Intuition

The most direct way to validate the board is to check each constraint one at a time. First, scan every row and make sure no digit repeats. Then scan every column. Then scan every 3x3 sub-box.

This mirrors exactly how a human would verify a Sudoku board: pick up a row, look for duplicates, then move to the next row. Repeat for columns and boxes.

Algorithm

  1. For each of the 9 rows, collect all non-empty cells and check for duplicates using a set.
  2. For each of the 9 columns, do the same.
  3. For each of the 9 sub-boxes (identified by box-row 0-2 and box-column 0-2), iterate over the 3x3 region and check for duplicates.
  4. If any duplicate is found in any check, return false. Otherwise, return true.

Code

The three-pass approach works perfectly fine, and since the board is fixed at 9x9, there's no real performance difference. But the code is repetitive. We write nearly identical duplicate-checking logic three times. What if we could do all three checks in a single pass over the board?

Approach 2: Single Pass with Hash Sets

Intuition

Instead of scanning the board three separate times, we can check all three constraints simultaneously. The idea is to maintain a set for every row, every column, and every 3x3 box. As we iterate through each cell once, we check whether the digit already exists in its row's set, its column's set, or its box's set.

The key insight is how to identify which box a cell belongs to. For a cell at position (r, c), integer division gives us the answer: r / 3 tells us the box's row index (0, 1, or 2), and c / 3 tells us the box's column index. So cell (5, 7) belongs to box (1, 2), which is the middle-right box. We can combine these into a single box index: (r / 3) * 3 + (c / 3), which gives a unique number from 0 to 8 for each of the nine boxes.

Algorithm

  1. Create 9 sets for rows, 9 sets for columns, and 9 sets for boxes.
  2. Iterate through every cell (r, c) on the board.
  3. If the cell is empty (.), skip it.
  4. Compute the box index as (r / 3) * 3 + (c / 3).
  5. If the digit is already in the row set, column set, or box set, return false.
  6. Otherwise, add the digit to all three sets.
  7. If we finish without finding duplicates, return true.

Code

The hash set approach works well, but hash sets have overhead: hashing, dynamic memory allocation, and pointer chasing. Since we're only tracking digits 1-9, we could use a fixed-size boolean array or even a single integer as a bitmask. Each digit maps to a bit position, and checking for duplicates becomes a single bitwise AND. This eliminates the hash set overhead entirely.

Approach 3: Bitmask (Optimal Space Efficiency)

Intuition

Since we only have digits 1 through 9, we can represent the "seen digits" for any row, column, or box as a single integer. Each bit in the integer corresponds to a digit. Bit 1 represents digit '1', bit 2 represents digit '2', and so on up to bit 9 for digit '9'.

To check if a digit has been seen, we use a bitwise AND. To mark a digit as seen, we use a bitwise OR. This replaces hash set operations with single CPU instructions, which is as fast as it gets.

Algorithm

  1. Create three arrays of 9 integers: rows, cols, and boxes, all initialized to 0.
  2. Iterate through every cell (r, c) on the board.
  3. If the cell is empty, skip it.
  4. Compute mask = 1 << (digit - '1') to get the bitmask for this digit.
  5. Compute the box index as (r / 3) * 3 + (c / 3).
  6. If rows[r] & mask, cols[c] & mask, or boxes[boxIdx] & mask is non-zero, the digit is a duplicate. Return false.
  7. Otherwise, set the bit: rows[r] |= mask, cols[c] |= mask, boxes[boxIdx] |= mask.
  8. If we finish without finding duplicates, return true.

Code