Check board configuration is valid or not – Valid Sudoku
Check Sudoku board configuration is valid or not – Medium Level
You are given a a 9 x 9 Sudoku board board. A Sudoku board is valid if the following rules are followed:
Each row must contain the digits 1-9 without duplicates.
Each column must contain the digits 1-9 without duplicates.
Each of the nine 3 x 3 sub-boxes of the grid must contain the digits 1-9 without duplicates.
Return true if the Sudoku board is valid, otherwise return false
Note: A board does not need to be full or be solvable to be valid.
Examples of Valid Sudoku
[["1","2",".",".","3",".",".",".","."],
["4",".",".","5",".",".",".",".","."],
[".","9","8",".",".",".",".",".","3"],
["5",".",".",".","6",".",".",".","4"],
[".",".",".","8",".","3",".",".","5"],
["7",".",".",".","2",".",".",".","6"],
[".",".",".",".",".",".","2",".","."],
[".",".",".","4","1","9",".",".","8"],
[".",".",".",".","8",".",".","7","9"]]
Output: true
[["1","2",".",".","3",".",".",".","."],
["4",".",".","5",".",".",".",".","."],
[".","9","1",".",".",".",".",".","3"],
["5",".",".",".","6",".",".",".","4"],
[".",".",".","8",".","3",".",".","5"],
["7",".",".",".","2",".",".",".","6"],
[".",".",".",".",".",".","2",".","."],
[".",".",".","4","1","9",".",".","8"],
[".",".",".",".","8",".",".","7","9"]]
Output: false
Explanation: There are two 1’s in the top-left 3×3 sub-box.
Constraints:
- board.length == 9
- board[i].length == 9
- board[i][j] is a digit 1-9 or ‘.’.
Check Sudoku board configuration is valid or not Solution
Recommendation for Time and Space Complexity –You should aim for a solution as good or better than O(n^2) time and O(n^2) space, where n is the number of rows in the square grid.
Hints for solving problems
Hint 1 :
Which data structure would you prefer to use for checking duplicates?
Hint 2 :
You can use a hash set for every row and column to check duplicates. But how can you efficiently check for the squares?
Hint 3 :
We can find the index of each square by the equation (row / 3) * 3 + (col / 3). Then we use hash set for O(1) lookups while inserting the number into its row, column and square it belongs to. We use separate hash maps for rows, columns, and squares.
There are mainly 3 approach to solve this problem-
- Brute Force Method
- Hash Set(One Pass) Method
- Bitmask Method
1. Brute Force Method
This method check each row, column, and 3×3 sub-box independently for duplicates by iterating through the board multiple times. Simple but slow for larger grids.
- Time complexity: O(n^2)
- Space complexity: O(n^2)
Code
class Solution { public: bool isValidSudoku(vector>& board) { for (int row = 0; row < 9; row++) { unordered_set seen; for (int i = 0; i < 9; i++) { if (board[row][i] == '.') continue; if (seen.count(board[row][i])) return false; seen.insert(board[row][i]); } } for (int col = 0; col < 9; col++) { unordered_set seen; for (int i = 0; i < 9; i++) { if (board[i][col] == '.') continue; if (seen.count(board[i][col])) return false; seen.insert(board[i][col]); } } for (int square = 0; square < 9; square++) { unordered_set seen; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { int row = (square / 3) * 3 + i; int col = (square % 3) * 3 + j; if (board[row][col] == '.') continue; if (seen.count(board[row][col])) return false; seen.insert(board[row][col]); } } } return true; } };
public class Solution { public boolean isValidSudoku(char[][] board) { for (int row = 0; row < 9; row++) { Setseen = new HashSet<>(); for (int i = 0; i < 9; i++) { if (board[row][i] == '.') continue; if (seen.contains(board[row][i])) return false; seen.add(board[row][i]); } } for (int col = 0; col < 9; col++) { Set seen = new HashSet<>(); for (int i = 0; i < 9; i++) { if (board[i][col] == '.') continue; if (seen.contains(board[i][col])) return false; seen.add(board[i][col]); } } for (int square = 0; square < 9; square++) { Set seen = new HashSet<>(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { int row = (square / 3) * 3 + i; int col = (square % 3) * 3 + j; if (board[row][col] == '.') continue; if (seen.contains(board[row][col])) return false; seen.add(board[row][col]); } } } return true; } }
class Solution: def isValidSudoku(self, board: List[List[str]]) -> bool: for row in range(9): seen = set() for i in range(9): if board[row][i] == ".": continue if board[row][i] in seen: return False seen.add(board[row][i]) for col in range(9): seen = set() for i in range(9): if board[i][col] == ".": continue if board[i][col] in seen: return False seen.add(board[i][col]) for square in range(9): seen = set() for i in range(3): for j in range(3): row = (square//3) * 3 + i col = (square % 3) * 3 + j if board[row][col] == ".": continue if board[row][col] in seen: return False seen.add(board[row][col]) return True
class Solution { /** * @param {character[][]} board * @return {boolean} */ isValidSudoku(board) { for (let row = 0; row < 9; row++) { let seen = new Set(); for (let i = 0; i < 9; i++) { if (board[row][i] === '.') continue; if (seen.has(board[row][i])) return false; seen.add(board[row][i]); } } for (let col = 0; col < 9; col++) { let seen = new Set(); for (let i = 0; i < 9; i++) { if (board[i][col] === '.') continue; if (seen.has(board[i][col])) return false; seen.add(board[i][col]); } } for (let square = 0; square < 9; square++) { let seen = new Set(); for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { let row = Math.floor(square / 3) * 3 + i; let col = (square % 3) * 3 + j; if (board[row][col] === '.') continue; if (seen.has(board[row][col])) return false; seen.add(board[row][col]); } } } return true; } }
2. Hash Set Method(One Pass)
This method use a hash set to track the values in rows, columns, and sub-boxes as you iterate through the board in one pass. Efficient and easy to implement.
- Time complexity: O(n^2)
- Space complexity: O(n^2)
Code
class Solution { public: bool isValidSudoku(vector>& board) { unordered_map > rows, cols; map , unordered_set > squares; for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { if (board[r][c] == '.') continue; pair squareKey = {r / 3, c / 3}; if (rows[r].count(board[r][c]) || cols[c].count(board[r][c]) || squares[squareKey].count(board[r][c])) { return false; } rows[r].insert(board[r][c]); cols[c].insert(board[r][c]); squares[squareKey].insert(board[r][c]); } } return true; } };
public class Solution { public boolean isValidSudoku(char[][] board) { Map> cols = new HashMap<>(); Map > rows = new HashMap<>(); Map > squares = new HashMap<>(); for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { if (board[r][c] == '.') continue; String squareKey = (r / 3) + "," + (c / 3); if (rows.computeIfAbsent(r, k -> new HashSet<>()).contains(board[r][c]) || cols.computeIfAbsent(c, k -> new HashSet<>()).contains(board[r][c]) || squares.computeIfAbsent(squareKey, k -> new HashSet<>()).contains(board[r][c])) { return false; } rows.get(r).add(board[r][c]); cols.get(c).add(board[r][c]); squares.get(squareKey).add(board[r][c]); } } return true; } }
class Solution: def isValidSudoku(self, board: List[List[str]]) -> bool: cols = defaultdict(set) rows = defaultdict(set) squares = defaultdict(set) for r in range(9): for c in range(9): if board[r][c] == ".": continue if ( board[r][c] in rows[r] or board[r][c] in cols[c] or board[r][c] in squares[(r // 3, c // 3)]): return False cols[c].add(board[r][c]) rows[r].add(board[r][c]) squares[(r // 3, c // 3)].add(board[r][c]) return True
class Solution { /** * @param {character[][]} board * @return {boolean} */ isValidSudoku(board) { const cols = new Map(); const rows = new Map(); const squares = new Map(); for (let r = 0; r < 9; r++) { for (let c = 0; c < 9; c++) { if (board[r][c] === '.') continue; const squareKey = `${Math.floor(r / 3)},${Math.floor(c / 3)}`; if ((rows.get(r) && rows.get(r).has(board[r][c])) || (cols.get(c) && cols.get(c).has(board[r][c])) || (squares.get(squareKey) && squares.get(squareKey).has(board[r][c]))) { return false; } if (!rows.has(r)) rows.set(r, new Set()); if (!cols.has(c)) cols.set(c, new Set()); if (!squares.has(squareKey)) squares.set(squareKey, new Set()); rows.get(r).add(board[r][c]); cols.get(c).add(board[r][c]); squares.get(squareKey).add(board[r][c]); } } return true; } }
3. Bitmask Method
This method represent rows, columns, and sub-boxes using bitmasks to efficiently track and validate numbers, reducing space usage and improving performance.
- Time complexity: O(n^2)
- Space complexity: O(n)
Code
class Solution { public: bool isValidSudoku(vector>& board) { int rows[9] = {0}; int cols[9] = {0}; int squares[9] = {0}; for (int r = 0; r < 9; ++r) { for (int c = 0; c < 9; ++c) { if (board[r][c] == '.') continue; int val = board[r][c] - '1'; if ((rows[r] & (1 << val)) || (cols[c] & (1 << val)) || (squares[(r / 3) * 3 + (c / 3)] & (1 << val))) { return false; } rows[r] |= (1 << val); cols[c] |= (1 << val); squares[(r / 3) * 3 + (c / 3)] |= (1 << val); } } return true; } };
public class Solution { public boolean isValidSudoku(char[][] board) { int[] rows = new int[9]; int[] cols = new int[9]; int[] squares = new int[9]; for (int r = 0; r < 9; r++) { for (int c = 0; c < 9; c++) { if (board[r][c] == '.') continue; int val = board[r][c] - '1'; if ((rows[r] & (1 << val)) > 0 || (cols[c] & (1 << val)) > 0 || (squares[(r / 3) * 3 + (c / 3)] & (1 << val)) > 0) { return false; } rows[r] |= (1 << val); cols[c] |= (1 << val); squares[(r / 3) * 3 + (c / 3)] |= (1 << val); } } return true; } }
class Solution: def isValidSudoku(self, board: List[List[str]]) -> bool: rows = [0] * 9 cols = [0] * 9 squares = [0] * 9 for r in range(9): for c in range(9): if board[r][c] == ".": continue val = int(board[r][c]) - 1 if (1 << val) & rows[r]: return False if (1 << val) & cols[c]: return False if (1 << val) & squares[(r // 3) * 3 + (c // 3)]: return False rows[r] |= (1 << val) cols[c] |= (1 << val) squares[(r // 3) * 3 + (c // 3)] |= (1 << val) return True
class Solution { isValidSudoku(board) { let rows = new Array(9).fill(0); let cols = new Array(9).fill(0); let squares = new Array(9).fill(0); for (let r = 0; r < 9; r++) { for (let c = 0; c < 9; c++) { if (board[r][c] === '.') continue; let val = board[r][c] - '1'; if ((rows[r] & (1 << val)) || (cols[c] & (1 << val)) || (squares[Math.floor(r / 3) * 3 + Math.floor(c / 3)] & (1 << val))) { return false; } rows[r] |= (1 << val); cols[c] |= (1 << val); squares[Math.floor(r / 3) * 3 + Math.floor(c / 3)] |= (1 << val); } } return true; } }