BackTrackingProblem 5 of 19
Sudoku Solver
Problem Statement
Write a program to solve a Sudoku puzzle by filling the empty cells. A sudoku solution must satisfy all of the following rules:
- Each of the digits 1-9 must occur exactly once in each row.
- Each of the digits 1-9 must occur exactly once in each column.
- Each of the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes.
The '.' character indicates empty cells.
Example:
Input:
[["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:
[["5","3","4","6","7","8","9","1","2"],
["6","7","2","1","9","5","3","4","8"],
["1","9","8","3","4","2","5","6","7"],
["8","5","9","7","6","1","4","2","3"],
["4","2","6","8","5","3","7","9","1"],
["7","1","3","9","2","4","8","5","6"],
["9","6","1","5","3","7","2","8","4"],
["2","8","7","4","1","9","6","3","5"],
["3","4","5","2","8","6","1","7","9"]]
Approach 1: Brute Force (Try All Digits)
Intuition
For each empty cell, try all digits from 1 to 9. For each digit, check if placing it violates any Sudoku rule. If valid, place it and move to the next empty cell. Backtrack if stuck.
Algorithm
- Find the next empty cell
- Try digits 1-9 in that cell
- For each digit, check if it's valid (not in same row, column, or 3x3 box)
- If valid, place it and recurse
- If recursion returns false, backtrack (reset to empty)
- If all cells filled, return true
java
class Solution {
public boolean isValid(char[][] board, int row, int col, char c) {
for (int i = 0; i < 9; i++) {
// Check row
if (board[row][i] == c) return false;
// Check column
if (board[i][col] == c) return false;
// Check 3x3 box
int boxRow = 3 * (row / 3) + i / 3;
int boxCol = 3 * (col / 3) + i % 3;
if (board[boxRow][boxCol] == c) return false;
}
return true;
}
public boolean solve(char[][] board) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.') {
// Try digits 1-9
for (char c = '1'; c <= '9'; c++) {
if (isValid(board, i, j, c)) {
board[i][j] = c;
if (solve(board)) {
return true;
}
board[i][j] = '.'; // Backtrack
}
}
return false; // No valid digit found
}
}
}
return true; // All cells filled
}
public void solveSudoku(char[][] board) {
solve(board);
}
}Complexity Analysis
- Time Complexity: O(9^(n²)) - In worst case, try 9 digits for each of 81 cells
- Space Complexity: O(n²) - Recursion stack depth
Approach 2: Optimal (Using Hash Sets for O(1) Validation)
Intuition
Pre-compute which digits are already present in each row, column, and box using hash sets. This reduces validation from O(n) to O(1) for each placement.
Algorithm
- Initialize sets for each row, column, and box
- Pre-populate sets with existing digits
- For each empty cell, check sets instead of iterating
- Update sets when placing/removing digits
java
import java.util.*;
class Solution {
private Set<Character>[] rows = new HashSet[9];
private Set<Character>[] cols = new HashSet[9];
private Set<Character>[] boxes = new HashSet[9];
private int getBoxIndex(int row, int col) {
return (row / 3) * 3 + col / 3;
}
public boolean solve(char[][] board, int row, int col) {
// Move to next row if column exceeds
if (col == 9) {
row++;
col = 0;
}
// All cells filled
if (row == 9) return true;
// Skip filled cells
if (board[row][col] != '.') {
return solve(board, row, col + 1);
}
int boxIdx = getBoxIndex(row, col);
// Try digits 1-9
for (char c = '1'; c <= '9'; c++) {
if (rows[row].contains(c) || cols[col].contains(c) || boxes[boxIdx].contains(c)) {
continue;
}
// Place digit
board[row][col] = c;
rows[row].add(c);
cols[col].add(c);
boxes[boxIdx].add(c);
if (solve(board, row, col + 1)) {
return true;
}
// Backtrack
board[row][col] = '.';
rows[row].remove(c);
cols[col].remove(c);
boxes[boxIdx].remove(c);
}
return false;
}
public void solveSudoku(char[][] board) {
// Initialize sets
for (int i = 0; i < 9; i++) {
rows[i] = new HashSet<>();
cols[i] = new HashSet<>();
boxes[i] = new HashSet<>();
}
// Populate sets with existing digits
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.') {
char c = board[i][j];
rows[i].add(c);
cols[j].add(c);
boxes[getBoxIndex(i, j)].add(c);
}
}
}
solve(board, 0, 0);
}
}Complexity Analysis
- Time Complexity: O(9^m) where m is number of empty cells - Much faster in practice
- Space Complexity: O(1) - Fixed 27 sets of max 9 elements each
Key Takeaways
- Box Index Formula:
(row/3)*3 + col/3gives the 3x3 box index (0-8) - Hash Set Optimization: Pre-computing constraints reduces validation time
- Cell-by-Cell: Process cells sequentially, backtrack when stuck
- Constraint Propagation: Could further optimize using techniques like naked singles
- LeetCode #37: Hard problem - Sudoku Solver
- Unique Solution: Standard Sudoku puzzles have exactly one solution