Sudoku Solver Using Backtracking in DSA Java - Time & Space Complexity
We want to understand how the time needed to solve a Sudoku puzzle changes as the puzzle size or difficulty changes.
Specifically, how does the backtracking method's work grow with the number of empty cells?
Analyze the time complexity of the following Sudoku solver using backtracking.
public boolean solveSudoku(int[][] board) {
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
if (board[row][col] == 0) {
for (int num = 1; num <= 9; num++) {
if (isValid(board, row, col, num)) {
board[row][col] = num;
if (solveSudoku(board)) return true;
board[row][col] = 0;
}
}
return false;
}
}
}
return true;
}
This code tries numbers 1 to 9 in empty cells and uses recursion to fill the board correctly.
Look at the loops and recursion that repeat work.
- Primary operation: Trying numbers 1 to 9 in each empty cell and recursively solving the rest.
- How many times: For each empty cell, up to 9 attempts, and recursion explores many combinations.
As the number of empty cells increases, the solver tries many combinations, growing very fast.
| Empty Cells (n) | Approx. Operations |
|---|---|
| 10 | Up to 9^10 (about 3.5 billion) attempts in worst case |
| 20 | Up to 9^20 (very large, trillions of trillions) |
| 30 | Up to 9^30 (extremely large, practically impossible to brute force) |
Pattern observation: The number of attempts grows exponentially with the number of empty cells.
Time Complexity: O(9^n)
This means the time needed grows exponentially with the number of empty cells, making it very slow for many empty spots.
[X] Wrong: "The solver tries only one number per empty cell, so it runs in linear time."
[OK] Correct: The solver tries many numbers per cell and uses recursion, so it explores many possible boards, causing exponential growth.
Understanding this exponential growth helps you explain why backtracking is powerful but can be slow, and why pruning or smarter methods matter.
"What if we limit the numbers tried per cell to only valid candidates instead of all 1 to 9? How would the time complexity change?"
Practice
Solution
Step 1: Understand backtracking role
Backtracking tries numbers in empty cells and removes them if they cause conflicts.Step 2: Compare with other options
Sorting or random filling does not solve Sudoku logically; counting empty cells is not solving.Final Answer:
To try possible numbers and undo choices if they lead to conflicts -> Option BQuick Check:
Backtracking = Try and undo choices [OK]
- Thinking backtracking sorts numbers
- Assuming backtracking fills randomly
- Confusing backtracking with counting cells
Solution
Step 1: Check method return type and parameters
The solver should return boolean to indicate success and take the board as input.Step 2: Validate options
public boolean solveSudoku(int[][] board) matches this: returns boolean and takes int[][] board. void solveSudoku(int board[][]) returns void, not suitable. public int solveSudoku(int[][] board, int row, int col) returns int and has extra parameters, uncommon. boolean solveSudoku() has no parameters, so can't solve given board.Final Answer:
public boolean solveSudoku(int[][] board) -> Option AQuick Check:
Boolean return + board parameter = public boolean solveSudoku(int[][] board) [OK]
- Using void return type for solver
- Missing board parameter
- Returning int instead of boolean
solveSudoku(board) on a partially filled 9x9 Sudoku board?int[][] board = {
{5,3,0,0,7,0,0,0,0},
{6,0,0,1,9,5,0,0,0},
{0,9,8,0,0,0,0,6,0},
{8,0,0,0,6,0,0,0,3},
{4,0,0,8,0,3,0,0,1},
{7,0,0,0,2,0,0,0,6},
{0,6,0,0,0,0,2,8,0},
{0,0,0,4,1,9,0,0,5},
{0,0,0,0,8,0,0,7,9}
};
boolean solved = solveSudoku(board);
System.out.println(solved);Solution
Step 1: Understand the input board
The given board is a valid Sudoku puzzle with empty cells (0) and solvable configuration.Step 2: Analyze solveSudoku output
Backtracking solver returns true if it finds a solution. Since this is a standard solvable puzzle, it returns true.Final Answer:
true -> Option CQuick Check:
Solvable board returns true [OK]
- Assuming false means no solution without checking
- Confusing output with board print
- Expecting exceptions on valid input
public boolean solveSudoku(int[][] board) {
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
if (board[row][col] == 0) {
for (int num = 1; num <= 9; num++) {
if (isSafe(board, row, col, num)) {
board[row][col] = num;
if (solveSudoku(board)) {
return true;
} else {
board[row][col] = 0;
}
}
}
return false;
}
}
}
return true;
}Solution
Step 1: Analyze the nested loops and recursion
The code tries numbers 1-9 in empty cells and recurses if safe.Step 2: Identify missing return false
If no number fits, the function should return false to backtrack, but here it continues loops and returns true at end incorrectly.Final Answer:
Missing return false when no number fits in empty cell -> Option DQuick Check:
Backtracking needs return false on failure [OK]
- Forgetting to return false to backtrack
- Returning true too early
- Ignoring base case for solved board
Solution
Step 1: Understand naive backtracking
Trying numbers 1-9 in order for each cell is simple but slow for complex puzzles.Step 2: Identify heuristic improvement
Selecting the empty cell with fewest valid options (minimum remaining value heuristic) reduces branching and speeds solving.Step 3: Evaluate other options
Filling all with 1 or random assignment leads to many conflicts and inefficiency.Final Answer:
Use a heuristic to select the empty cell with the fewest possible valid numbers first -> Option AQuick Check:
Heuristic cell choice speeds backtracking [OK]
- Trying numbers blindly without heuristics
- Filling cells with fixed number initially
- Random assignments causing many retries
Ready to prove it?
Pass the test and this pattern moves unknown
