Determine if a 9 x 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:
- Each row must contain the digits
1-9without repetition. - Each column must contain the digits
1-9without repetition. - Each of the nine
3 x 3sub-boxes of the grid must contain the digits1-9without repetition.
Note:
- A Sudoku board (partially filled) could be valid but is not necessarily solvable.
- Only the filled cells need to be validated according to the mentioned rules.
Example 1:
Input: board = [[“5”,“3”,”.”,”.”,“7”,”.”,”.”,”.”,”.”] ,[“6”,”.”,”.”,“1”,“9”,“5”,”.”,”.”,”.”] ,[”.”,“9”,“8”,”.”,”.”,”.”,”.”,“6”,”.”] ,[“8”,”.”,”.”,”.”,“6”,”.”,”.”,”.”,“3”] ,[“4”,”.”,”.”,“8”,”.”,“3”,”.”,”.”,“1”] ,[“7”,”.”,”.”,”.”,“2”,”.”,”.”,”.”,“6”] ,[”.”,“6”,”.”,”.”,”.”,”.”,“2”,“8”,”.”] ,[”.”,”.”,”.”,“4”,“1”,“9”,”.”,”.”,“5”] ,[”.”,”.”,”.”,”.”,“8”,”.”,”.”,“7”,“9”]] Output: true
Example 2:
Input: board = [[“8”,“3”,”.”,”.”,“7”,”.”,”.”,”.”,”.”] ,[“6”,”.”,”.”,“1”,“9”,“5”,”.”,”.”,”.”] ,[”.”,“9”,“8”,”.”,”.”,”.”,”.”,“6”,”.”] ,[“8”,”.”,”.”,”.”,“6”,”.”,”.”,”.”,“3”] ,[“4”,”.”,”.”,“8”,”.”,“3”,”.”,”.”,“1”] ,[“7”,”.”,”.”,”.”,“2”,”.”,”.”,”.”,“6”] ,[”.”,“6”,”.”,”.”,”.”,”.”,“2”,“8”,”.”] ,[”.”,”.”,”.”,“4”,“1”,“9”,”.”,”.”,“5”] ,[”.”,”.”,”.”,”.”,“8”,”.”,”.”,“7”,“9”]] Output: false Explanation: Same as Example 1, except with the 5 in the top left corner being modified to 8. Since there are two 8’s in the top left 3x3 sub-box, it is invalid.
Constraints:
board.length == 9board[i].length == 9board[i][j]is a digit1-9or'.'.
Approach: Three Passes with Hash Sets (Optimal Solution)
Algorithm
- Validate all rows: each row must not contain duplicate digits
- Validate all columns: each column must not contain duplicate digits
- Validate all nine
3 x 3sub-boxes: each box must not contain duplicate digits - Skip cells containing
'.'
Time & Space Complexity
- Time Complexity: O(1) - the board is always
9 x 9, so the work is constant - Space Complexity: O(1) - only fixed-size hash sets are used
Java Implementation
import java.util.HashSet;
import java.util.Set;
public class ValidSudoku {
/**
* Determine if a 9x9 Sudoku board is valid.
* @param board 9x9 board with digits '1'-'9' or '.'
* @return true if the board is valid
*/
public static boolean isValidSudoku(char[][] board) {
// Check rows
for (int i = 0; i < 9; i++) {
Set<Character> seen = new HashSet<>();
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.' && !seen.add(board[i][j])) {
return false;
}
}
}
// Check columns
for (int j = 0; j < 9; j++) {
Set<Character> seen = new HashSet<>();
for (int i = 0; i < 9; i++) {
if (board[i][j] != '.' && !seen.add(board[i][j])) {
return false;
}
}
}
// Check 3x3 sub-boxes
for (int box = 0; box < 9; box++) {
Set<Character> seen = new HashSet<>();
int rowStart = (box / 3) * 3;
int colStart = (box % 3) * 3;
for (int i = rowStart; i < rowStart + 3; i++) {
for (int j = colStart; j < colStart + 3; j++) {
if (board[i][j] != '.' && !seen.add(board[i][j])) {
return false;
}
}
}
}
return true;
}
// Test method
public static void main(String[] args) {
char[][] board1 = {
{'5', '3', '.', '.', '7', '.', '.', '.', '.'},
{'6', '.', '.', '1', '9', '5', '.', '.', '.'},
{'.', '9', '8', '.', '.', '.', '.', '6', '.'},
{'8', '.', '.', '.', '6', '.', '.', '.', '3'},
{'4', '.', '.', '8', '.', '3', '.', '.', '1'},
{'7', '.', '.', '.', '2', '.', '.', '.', '6'},
{'.', '6', '.', '.', '.', '.', '2', '8', '.'},
{'.', '.', '.', '4', '1', '9', '.', '.', '5'},
{'.', '.', '.', '.', '8', '.', '.', '7', '9'}
};
System.out.println("Test case 1 output: " + isValidSudoku(board1)); // Expected: true
char[][] board2 = {
{'8', '3', '.', '.', '7', '.', '.', '.', '.'},
{'6', '.', '.', '1', '9', '5', '.', '.', '.'},
{'.', '9', '8', '.', '.', '.', '.', '6', '.'},
{'8', '.', '.', '.', '6', '.', '.', '.', '3'},
{'4', '.', '.', '8', '.', '3', '.', '.', '1'},
{'7', '.', '.', '.', '2', '.', '.', '.', '6'},
{'.', '6', '.', '.', '.', '.', '2', '8', '.'},
{'.', '.', '.', '4', '1', '9', '.', '.', '5'},
{'.', '.', '.', '.', '8', '.', '.', '7', '9'}
};
System.out.println("Test case 2 output: " + isValidSudoku(board2)); // Expected: false
}
}
Alternative Implementation with Encoded Keys (Single Pass)
A single pass can be done by encoding each constraint into a unique string key and storing them in one hash set. A duplicate appears when a key is seen twice.
import java.util.HashSet;
import java.util.Set;
public class ValidSudokuSinglePass {
public static boolean isValidSudoku(char[][] board) {
Set<String> seen = new HashSet<>();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char c = board[i][j];
if (c != '.') {
int boxIndex = (i / 3) * 3 + j / 3;
String rowKey = "row" + i + ":" + c;
String colKey = "col" + j + ":" + c;
String boxKey = "box" + boxIndex + ":" + c;
if (!seen.add(rowKey) || !seen.add(colKey) || !seen.add(boxKey)) {
return false;
}
}
}
}
return true;
}
}
Example Walkthrough
For example 1, every row, column, and 3x3 box contains only distinct digits, so the result is true.
For example 2, the top-left cell is changed from 5 to 8, making the top-left 3x3 sub-box contain two 8s. The 3 x 3 box check detects the duplicate and returns false.
Key Points
- Rule Set: Three rules to verify - rows, columns, and
3 x 3sub-boxes - Ignore Empty Cells: Cells with
'.'are skipped - Box Indexing: For cell
(i, j), the box index is(i / 3) * 3 + j / 3 - Constant Work: Since the board size is fixed at
9 x 9, complexity is constant time - Single Pass Option: Encode row/column/box constraints as unique keys to validate in one pass